Search
Write a publication
Pull to refresh

Сохранение Portable Anymap (PPM) на PHP

Предисловие

Однажды возникла необходимость распознавания небольшой числовой капчи: всегда 6 цифр, шума нет, но есть искажения (поворот и «линза»). Простой алгоритм распознавания не сработал достаточно хорошо, поэтому пришлось искать готовые программы. Среди них встретилась небольшая утилита gocr (http://jocr.sourceforge.net/). Но в качестве входного файла она захотела диковинный для меня формат — pnm/pgm/pbm/ppm. Как оказалось, этот неофициальный формат придуман для работы с изображениями на разных платформах. Формат прост в программировании, однако зачем тратить бесценное время попусту в современном мире? Я решил поделиться с вами небольшой функцией на PHP.

Функция int function convert2ppm(string$fname_input, string $ver) принимает на вход имя файла $fname_input в формате jpg или png (легко добавить другие форматы, если нужно) и тип выходного формата $ver из следующих:
P1, P2, P3 (текстовые), P4, P5, P6 (двоичные).

В результате работы функция сохраняет в эту же папку файл указанного формата.
Возвращаемый результат — размер сохраненного файла.

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

P.S.
Понимаю, что операции конвертации данных и особенно в больших объемах лучше проводить на более низкоуровневых языках, но иногда удобно взять и «прилепить» определенный функционал копированием лишь одной функции для вашего рабочего языка. В данном случае — PHP. Тем более, что в интернете такой функции раньше не было. Теперь есть. Хабр не даст ей засохнуть.

Ссылки

1. ru.wikipedia.org/wiki/Portable_anymap — красочное описание с примером
2. netpbm.sourceforge.net/doc/ppm.html — спецификация
3. www.daubnet.com/en/file-format-pbm — спецификация на английском, но более подробно

Код
function convert2ppm($fname_input, $ver) {
  $fname_input=strtolower($fname_input);
  $fname_output=substr($fname_input, 0, strpos($fname_input, '.')). '.ppm';

  // определяем формат, открываем файл
  if (strpos($fname_input, '.png')!==false)
    $im = imagecreatefrompng($fname_input);
  elseif (strpos($fname_input, '.jpg')!==false)
    $im = imagecreatefromjpeg($fname_input);
  $ver=strtoupper($ver);
  $w=imagesx($im);
  $h=imagesy($im);

  $white_value= ($ver=='P1' OR $ver=='P4') ? '' : ' 255'; // значение белого цвета
  if ($ver=='P1' OR $ver=='P2' OR $ver=='P3') { // текстовый формат
    $s="$ver $w $h$white_value"; // строка для записи
    for($y=0; $y<$h; $y++) {
      $s.="\n";
      for($x=0; $x<$w; $x++) {
        $rgb = imagecolorsforindex($im, imageColorAt($im, $x, $y));
        switch ($ver) {
        case "P1": // ч/б изображение - 1 или 0 на пиксел (но в файле все равно ASCII-символ занимает 1 байт)
          $s.=(0.3*$rgb['red']+0.59*$rgb['green']+0.11*$rgb['blue']<128) ? '1 ' : '0 ';
          break;
        case "P2": // серое изображение 1 байт на пиксел
          $s.=round( (0.3*$rgb['red']+0.59*$rgb['green']+0.11*$rgb['blue'])).' ';
          break;
        case "P3": // цветное изображение 3 байта на пиксел
          $s.="{$rgb['red']} {$rgb['green']} {$rgb['blue']}\n";
          break;
        };
      };
      $s=trim($s);
    };
  }
  else { // двоичный формат
    $s="$ver $w $h$white_value"; // строка для записи
    for($y=0; $y<$h; $y++) {
      $b=''; // двоичная строка битов для P4
      for($x=0; $x<$w; $x++) {
        $rgb = imagecolorsforindex($im, imageColorAt($im, $x, $y));
        switch ($ver) {
        case "P4":
          $b.=(0.3*$rgb['red']+0.59*$rgb['green']+0.11*$rgb['blue']<128) ? '1' : '0';
          break;
        case "P5": // изображение оттенками серого формата 1байт на пиксел
          $s.=pack('c', round(0.3*$rgb['red']+0.59*$rgb['green']+0.11*$rgb['blue']));
          break;
        case "P6": // цветное изображение 3 байта на пиксел
          $s.=pack('ccc', $rgb['red'], $rgb['green'], $rgb['blue']);
          break;
        };
      };
      if ($ver=='P4') {
        // дополняем последний байт
        $d=strlen($b) % 8;
        $b=substr($b, 0, -$d). str_pad(substr($b, -$d), 8, '0', STR_PAD_LEFT);
        // каждые 8бит упаковываем в байт
        for($i=0; $i<strlen($b); $i+=8)
          $s.=pack('c', bindec(substr($b, $i, 8))); // пишем упакованный байт в строку
      };
    };
  };

  imagedestroy($im);
  return file_put_contents($fname_output, $s);
};

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.