Добро пожаловать на авторский блог Авдеева Марка.

Только для читателей Lifeexample возможно открыть интернет-магазин на Moguta.CMS со скидкой в 15%

<<< Полнотекстовый поиск MySQL || Что такое стартап. История моего стартап-проекта. >>>

PHP работа с изображениями

11.04.2013
PHP работа с изображениями

Здравствуйте, уважаемые читатели блога LifeExample, прошло много времени с публикации предыдущей статьи и вот у меня появилось время пополнить коллекцию моих публикаций новым уникальным и полезным контентом. В этом посте я хочу привести пример работы с изображениями на php. Данная статья навеяна мне задачами по разработке интернет магазина на Moguta.CMS.

PHP работа с изображениями описана во многих источниках с подробным рассмотрением допустимых методов, но мне не встретилось ни одно материала о их практическом применении.

Научившись применять основные из функций php библиотеки GD (Graphics Draw) я нашел ей применение в решении одной задачи, суть которой сводится к выравниванию масштаба всех изображений в наборе.

Дано: набор изображений для товаров интернет магазина.

исходный набор картинок для php работы с изображениями

Если внимательно посмотреть на приведенные изображения, то мы увидим, что по габаритам они отличаются не только размерами холстов, на которых расположена продукция, но и сами товары имеют разные размеры.

Задача: нормализовать все картинки и получить из выше приведенного набора следующий результат.

результат php работы с картинками

Получившиеся изображения товаров должны быть, все одного разрешения и одного масштаба.

Решение задачи ( PHP работа с изображениями )

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

Давайте определимся, какую последовательность действий нам нудно выполнить, чтобы достичь желаемого результата, и выровнять все имеющиеся изображения товаров в магазине.

  1. Вычислить размеры холста изображения;
  2. Вычислить ширину и высоту товара на изображении;
  3. Вычислить пропорции для сжатия холста вместе с изображением товара;
  4. Если товар на холсте меньше эталонного, то не сжимать его;
  5. Создать итоговый холст с нужными размерами и поместить в его центр получившуюся при сжатии картинку.

Выполнив каждый пункт текущего алгоритма, на выходе мы получим нормализованные картинки одинакового размера и масштаба.

Как вычислить размеры внутреннего рисунка на холсте

Самой сложной из подзадач является, поиск габаритов изображения:

схема координат поиска размеров изображения

На приведенном рисунке

  • P0 – точка начала области изображения товара
  • H1- высота товара
  • W1- ширина товара
  • H2- расстояние от верхней границы холста до точки P0
  • W2- расстояние от левой границы холста до точки P0
  • H3- высота всего изображения картинки товара
  • W3- ширина всего изображения картинки товара

Чтобы определить все неизвестные нам параметры, мы воспользуемся как встроенными средствами PHP по работе с изображениями, так и собственным "велосипедом".

Найти H3 и W3 можно php функцией getimagesize(), но как бы нам вычислить H1 и W1? Что если мы вернемся к статье по распознаванию капчи? Возьмем из этой статьи алгоритм преобразования изображения в бинарную матрицу.

Бинарная матрица – это представления изображения в виде единиц и нулей, все пикселы изображения отличающиеся от фонового цвета получат значение = 0 , а пикселы составляющие само изображение будут равны 1, в итоге мы получим такую матрицу:

матричное представление изображения

Теперь мы можем запросто вычислить интервалы, в которых находятся 1-цы, о том как это сделать читайте в статье по распознаванию капчи.

После того как нам станут известны все необходимые параметры, можно будет выполнять наш алгоритм нормализации картинок.

В готовом виде PHP скрипт будет выглядеть так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
<?php
/**
 * Класс нормальзующий картинку по заданному разрешению
 */

class imageWorker {

  public $baseWidth = 150;  // длина эталонного изображения (именно товара внутри картинки)
  public $baseHeight = 119; // высота эталонного изображения (именно товара внутри картинки)
  public $outImageWidth = 600;  // длина изображения после обработки
  public $outImageHeight = 400; // высота изображения после обработки
  public $im = null;  // дескриптор картитнки
  public $binaryMartix = null; // матричное представление картитнки
  public $saveBinaryMartixTofile = false; // сохранять матричное представление картитнки в файл
  public $dir = 'image/source/';  // расположения набора картинок
  public $saveMartix = false; // сохранять матричное представление картитнок
  public $extensions = array("", "gif", "jpeg", "png"); // допустимые картинки
  public $curImageWidth = 1;  // ширина обрабатываемого изображения
  public $curImageHeight = 1; // высота обрабатываемого изображения
  public $imgFunctionProcess = "imagecreatefromjpeg"; // функция для работы с изображением
  public $curExt = ""; // расширение картинки
  public $curImageName = ""; // расширение картинки
  public $dirUpload = 'image/result/'; // папка для выгрузки (должна быть создана)


  function __construct($path, $w, $h) {
    $this->outImageWidth = $w;
    $this->outImageHeight = $h;
    $this->curImageName = $path;

    list($this->curImageWidth, $this->curImageHeight, $type) = getimagesize($this->dir.$path); // Получаем размеры и тип изображения (число)
    $ext = $this->extensions[$type];
    if ($ext) {
      $this->imgFunctionProcess = 'imagecreatefrom'.$ext; // Получаем название функции, соответствующую типу, для создания изображения
      $func = $this->imgFunctionProcess;
      $this->curExt = $ext;
      $this->im = $func($this->dir.$path); // Создаём дескриптор для работы с исходным изображением
      if (!$this->im) {
        return false;
      }
    } else {
      echo 'Ошибка: Неизвестный формат изображения!';
      return false;
    }

    $this->binaryMartix = $this->imageToMatrix($this->im, false);

    if ($this->saveBinaryMartixTofile) {
      $this->printMatrix($this->binaryMartix);
    }

    $res = $this->explodeMatrix($this->binaryMartix);
    $width = $res['resultInterval'];
    $cropX = $res['startInterval'];

    $this->binaryMartix = $this->imageToMatrix($this->im, true);
    $res = $this->explodeMatrix($this->binaryMartix);
    $height = $res['resultInterval'];
    $cropY = $res['startInterval'];

    $result = "Размеры изображения (".$path.") <br/>width=".$this->curImageWidth."px; <br/>   height=".$this->curImageHeight."px;";
    $result .= "<br/>Размеры изделия внутри изображения <br/>width=".$width."px; <br/>   height=".$height."px;";
    $result .= "<br/>Коэффициенты сжатия <br/>width=".$this->baseWidth / $width."px; <br/>    height=".$this->baseHeight / $height."px;";
    $result .= "<br/>Отрезать картинку с точки <br/>cropX=".$cropX." <br/>    cropY=".$cropY." ";

    //$this->crop("2.png", $cropY, $cropX, $width, $height); // Вызываем функцию
    //$this->reSizeImage($name, $ext, $tmp, 0.3);
    echo $result;
    if ($this->baseHeight < $height) {
      $this->resizeImage($this->baseHeight / $height);
    } else {
      $this->resizeImage(1);
    };

    imagedestroy($this->im);
  }


  function explodeMatrix($binaryMartix) {
    $temp = array();

    // сложение столбцов для выявления интервалов
    for ($i = 0; $i < count($binaryMartix); $i++) {
      $sum = 0;
      for ($j = 0; $j < count($binaryMartix[0]); $j++) {
        $sum += $binaryMartix[$i][$j];
      }
      $temp[] = $sum ? 1 : 0;
    }


    // вычисление интервалов по полученной строке
    $start = false;
    $countPart = 0;
    $arrayInterval = array();
    foreach ($temp as $k => $v) {

      if ($v == 1 && !$start) {
        $arrayInterval[$countPart]['start'] = $k;
        $start = true;
      }

      if ($v == 0 && $start) {
        $arrayInterval[$countPart]['end'] = $k - 1;
        $start = false;
        $countPart++;
      }
    }

    //отсеиваем помехи (мелкие интервалы), Большая картинка, всяко больше 20px.

    $resultInterval = 1;
    $startInterval = 1; // начало интервала
    foreach ($arrayInterval as $key => $interval) {
      if (($interval['end'] - $interval['start']) > 20) {
        $resultInterval = $interval['end'] - $interval['start'];
        $startInterval = $interval['start'];
      }
    }
    return
      array(
        'resultInterval' => $resultInterval,
        'startInterval' => $startInterval
    );
  }


  /**
   * Конвертация рисунка в бинарную матрицу
   * Все пиксели отличные от фона получают значение 1
   * @param imagecreatefrompng $im - картинка в формате PNG
   * @param bool $rotate - горизонтальная или вертикальная матрица
   */

  function imageToMatrix($im, $rotate = false) {
    $height = imagesy($im);
    $width = imagesx($im);

    if ($rotate) {
      $height = imagesx($im);
      $width = imagesy($im);
    }

    $background = 0;
    for ($i = 0; $i < $height; $i++)
      for ($j = 0; $j < $width; $j++) {

        if ($rotate) {
          $rgb = imagecolorat($im, $i, $j);
        } else {
          $rgb = imagecolorat($im, $j, $i);
        }

        //получаем индексы цвета RGB
        list($r, $g, $b) = array_values(imageColorsForIndex($im, $rgb));

        //вычисляем индекс красного, для фона изображения
        if ($i == 0 && $j == 0) {
          $background = $r;
        }
        //echo "red=".$background;
        $sensitivity = 15;
        // если цвет пикселя не равен фоновому заполняем матрицу единицей    
        $binary[$i][$j] = ($r > $background - $sensitivity) ? 0 : 1;
      }
    return $binary;
  }


  /**
   * Выводит матрицу на экран
   * @param type $binaryMartix
   */

  function printMatrix($binaryMartix) {
    $return = '';
    for ($i = 0; $i < count($binaryMartix); $i++) {
      $return .= "\n";
      for ($j = 0; $j < count($binaryMartix[0]); $j++) {
        $return .= $binaryMartix[$i][$j]." ";
      }
    }
    file_put_contents($this->dirUpload.$this->curImageName.".txt", $return);
  }


  /**
   * Функция для ресайза картинки
   * @paramint $koef коэффициент сжатия изображения
   * @return void
   */

  public function resizeImage($koef) {
    // получение новых размеров  
    $newWidth = $koef * $this->curImageWidth;
    $newHeight = $koef * $this->curImageHeight;
    // ресэмплирование    
    $image_p = imagecreatetruecolor($this->outImageWidth, $this->outImageHeight);
    //делаем фон изображения белым, иначе в png при прозрачных рисунках фон черный
    $color = imagecolorallocate($image_p, 255, 255, 255);
    imagefill($image_p, 0, 0, $color);
    imagecopyresampled(
      $image_p, $this->im, ($this->outImageWidth - $newWidth) / 2, ($this->outImageHeight - $newHeight) / 2, 0, 0, $newWidth, $newHeight, $this->curImageWidth, $this->curImageHeight
    );
    $func = "image".$this->curExt;
    $func($image_p, $this->dirUpload.$this->curImageName);
    imagedestroy($image_p);
  }

}// конец тела класса

/**
 * Массив имен файлов
 */

$imagesName = array(
  'kameya_medium_1005858593.jpg',
  'kameya_medium_1005862834.jpg',
  'km0210-3_small.jpg',
  'sd0201966_small.jpg',
);

/**
 * Перебор массива имен файлов и нормализация изображений
 */

foreach ($imagesName  as $image) {
  if (file_exists('image/source/'.$image)) {
     // каждую картинку нормальзуем и приведем к разрешению 200x200 пикселей
     $encrypt = new imageWorker($image, 200, 200);
  } else {
    continue;
  }
}

Данный скрипт PHP работает с изображениями из папки image/source/ и по результату своего выполнения складывает нормализованные картинки в папку image/result/.

Скачать исходники примера ( Скачали: 277 чел. ) 

Выполнив поставленную задачу и на практике познакомившись с тем, как происходит в php работа с изображениями, мы получили опыт на основе, которого сможем приводить изображения товаров каталога в приятный вид.

Чтобы не пропустить публикацию следующей статьи подписывайтесь на рассылку по E-mail или RSS ленту блога.

Нравится

Комментарии

  • Влад

    Напишите статью про создание собственной системы подписки на PHP и MySQL

  • Михаил

    Работает только с определенными изображениями…

    • Да, есть некоторые ограничения. Но имеющихся форматов должно быть достаточно.

  • Оставить комментарий

    Подписаться на комментарии к этой статье по RSS

    Размещение статей и контекстных ссылок
    Бесплатная CMS для вашего магазина
    Яндекс.Метрика