Предисловие
Однажды возникла необходимость распознавания небольшой числовой капчи: всегда 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);
};