БлогNot. Делаем ASCII-картинку из PNG

Делаем ASCII-картинку из PNG

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

ASCII-графика хорошо показывает идею матрицы :)
ASCII-графика хорошо показывает идею матрицы :)

Тема ASCII-графики не нова и в сети можно найти разные подходы к её автоматической генерации. Ниже прилагается небольшой класс на PHP, предназначенный для изготовления чёрно-белого ASCII-образа из рисунка или фото в формате PNG.

Идея такого преобразования очень проста - рисунок попиксельно разбивается на образцы вида 0000, 0001, ...., 1110, 1111, затем образцам сопоставляются символы. Картинку для теста брал классическую, легко прикрутить к скрипту загрузку своего файла. В браузере матрица символов будет получаться довольно большой, так что уменьшаем размер комбинацией клавиш Ctrl+(серый_минус), вернуть потом масштаб по умолчанию можно будет комбинацией клавиш Ctrl+ноль (обычный).

Ниже показан исходник, проверенный на локальном хосте с PHP 7.X, предполагается, что скрипт будет сохранён в кодировке Юникода UTF-8.

<?php
class Ascii {
 protected function yiq($r,$g,$b) { return (($r*0.299)+($g*0.587)+($b*0.114)); }
  //См. https://ru.wikipedia.org/wiki/YIQ
    
 protected function getSample ($sample, $sample_mean, $mean, $sigma) {
  switch ($sample) {
   case '0000':
    if ($sample_mean >= $mean + $sigma  || $sample_mean >= 255) return ' ';
    else if ($sample_mean >= $mean + 0.33 * $sigma) return '.';
    else if ($sample_mean >= $mean) return '*';
   break;
   case '0001': case '0010': return ','; break;
   case '0011': case '0111': return '/'; break; 
   case '0101': case '1010': return '='; break; 
   case '0110': return '+'; break; 
   case '1000': case '0100': return '`'; break;
   case '1001': return '"'; break;
   case '1011':return '&gt;'; break;
   case '1100': case '1110': return '\\'; break;
   case '1101': return '&lt;'; break;
   case '1111':
    if ($sample_mean <= $mean - $sigma  || $sample_mean <= 0) return '@';
    else if ($sample_mean <= $mean - 0.33 * $sigma) return 'O';
    else if ($sample_mean <= $mean) return 'o';
   break;
  }
  return ' ';
 }
    
 public function draw ($image) {
  $result = '';
  $width = imagesx($image);
  $height = imagesy($image);
  $sum = 0;
  $count = 0;
  $yiq = array();
  for ($y=0;$y < $height;$y++) {
   for ($x=0;$x < $width;$x++) {
    $rgb = imagecolorat($image, $x, $y);
    $r = ($rgb >> 16) & 0xff;
    $g = ($rgb >> 8) & 0xff;
    $b = $rgb & 0xff;
    $offset = $y * $width + $x;
    $yiq[$offset] = $this->yiq($r,$g,$b);
    $sum += $yiq[$offset];
    $count++;
   }
  }
  $mean = $sum / $count; //среднее
  $sigma = 0; //среднеквадратичное отклонение
  foreach ($yiq as $k => $curr) $sigma += pow($curr - $mean, 2);
  $sigma = sqrt($sigma / $count);
  //считать всё попиксельно и вычислить среднее по образцу             
  for ($y=0; $y < $height - 2; $y+=4) { //шаг по y другой, т.к. символ ASCII не квавдратный
   for ($x=0; $x < $width - 2; $x+=2) {
    $sample = '';
    $sample_mean = 0;
    for ($i = $y; $i < $y + 2; $i++) {
     for ($j = $x; $j < $x + 2; $j++) {
      $rgb = imagecolorat($image, $j, $i);
      $r = ($rgb >> 16) & 0xff;
      $g = ($rgb >> 8) & 0xff;
      $b = $rgb & 0xff;
      $yiq1 = $yiq[$i * $width + $j];
      if ($yiq1 < $mean) $sample .= '1'; //черный
      else $sample .= '0'; //белый
      $sample_mean += $yiq1;
     }
    }
    $sample_mean /= 4;
    $result .= $this->getSample ($sample, $sample_mean, $mean, $sigma);
   }
   $result .= "\n";
  } 
  return $result;
 }
}

$filename = '1.png'; //получите свой файл или загрузите его из формы Lenna

$ascii = new Ascii();
$image = imagecreatefrompng($filename);
echo '<pre>'.$ascii->draw($image).'</pre>';
?>

Скрипт не создаёт стандартное HTML-обрамление страницы.


теги: виртуальность php графика форматы картинка символ

13.11.2020, 18:36; рейтинг: 67