Create a gist now

Instantly share code, notes, and snippets.

@oranj /url2ascii.php
Last active Dec 15, 2015

What would you like to do?
#!/usr/bin/php
<?php
/**
* Calibration: depending on your fonts and terminal colors, you may want to change these
*/
global $PIXELS, $COLORS, $HSV_COLORS;
$YSCALE = 12;
$XSCALE = 5;
$COLORS = array(
'd95136' => '1', // red
'e953e9' => '5', // purple
'6f4bf9' => '4', // blue
'42c9d6' => '6', // cyan
'38cb36' => '2', // green
'bcbb36' => '3' // yellow
);
$PIXELS = array(
'0.9' => '',
'0.8' => '',
'0.7' => '',
'0.6' => '#',
'0.5' => 'X',
'0.4' => 'T',
'0.3' => '>',
'0.2' => '-',
'0.1' => '.',
'0' => ' '
);
if ($argc != 2) {
echo "Missing URL";
die(1);
}
foreach ($COLORS as $rgb => $code) {
$r = hexdec($rgb[0].$rgb[1]);
$g = hexdec($rgb[2].$rgb[3]);
$b = hexdec($rgb[4].$rgb[5]);
$hsv = rgb_to_hsv($r, $g, $b);
$degrees = 60 * $hsv[0];
$HSV_COLORS[$degrees] = $code;
}
ksort($HSV_COLORS);
$HSV_COLORS[360 + key($HSV_COLORS)] = current($HSV_COLORS);
#drop($HSV_COLORS);
$url = $argv[1];
if ($url == 'calibrate') {
echo "These are your available colors. Please set the \$COLOR variable accordingly\n";
foreach ($COLORS as $color) {
echo "\033[0;3".$color."m▓▓▓▓▓▓▓▓▓▓ (3".$color.")\033[0m\n";
}
exit;
}
// Stolen from http://stackoverflow.com/a/13887939
function rgb_to_hsv($r, $g, $b) {
$R = (int)$r;
$G = (int)$g;
$B = (int)$b;
$key = sprintf("%x%x%x", $R, $G, $B);
static $cache = array();
if (! (isset($cache[$key]) && $out = $cache[$key])) {
// Convert the RGB byte-values to percentages
$R = ($R / 255);
$G = ($G / 255);
$B = ($B / 255);
// Calculate a few basic values, the maximum value of R,G,B, the
// minimum value, and the difference of the two (chroma).
$maxRGB = max($R, $G, $B);
$minRGB = min($R, $G, $B);
$chroma = $maxRGB - $minRGB;
// Value (also called Brightness) is the easiest component to calculate,
// and is simply the highest value among the R,G,B components.
// We multiply by 100 to turn the decimal into a readable percent value.
$computedV = $maxRGB;
// Special case if hueless (equal parts RGB make black, white, or grays)
// Note that Hue is technically undefined when chroma is zero, as
// attempting to calculate it would cause division by zero (see
// below), so most applications simply substitute a Hue of zero.
// Saturation will always be zero in this case, see below for details.
if ($chroma == 0)
return array(0, 0, $computedV);
// Saturation is also simple to compute, and is simply the chroma
// over the Value (or Brightness)
// Again, multiplied by 100 to get a percentage.
$computedS = ($chroma / $maxRGB);
// Calculate Hue component
// Hue is calculated on the "chromacity plane", which is represented
// as a 2D hexagon, divided into six 60 degree sectors. We calculate
// the bisecting angle as a value 0 <= x < 6, that represents which
// portion of which sector the line falls on.
if ($R == $minRGB)
$h = 3 - (($G - $B) / $chroma);
elseif ($B == $minRGB)
$h = 1 - (($R - $G) / $chroma);
else
$h = 5 - (($B - $R) / $chroma);
// After we have the sector position, we multiply it by the size of
// each sector's arc (60 degrees) to obtain the angle in degrees.
$computedH = $h;
$out = array($computedH, $computedS, $computedV);
}
return $out;
}
function see() {
print_r(func_get_args());
}
function drop() {
print_r(func_get_args());
exit;
}
$image = file_get_contents($url);
if (! $image) {
echo "Could not fetch image $url\n";
die(1);
}
$img = imagecreatefromstring($image);
#imagefilter($img, IMG_FILTER_EDGEDETECT);
$image_width = imagesx($img);
$image_height = imagesy($img);
$ascii_w_h_scale = $XSCALE / $YSCALE;
$img_scale = $image_width/$image_height;
$target_width = get_tty_width();
$target_height = ($target_width * $ascii_w_h_scale) / ($img_scale);
$ascii_x_scale = $image_width / $target_width;
$ascii_y_scale = $image_height / $target_height;
function brightness_from_index($rgb, &$_r, &$_g, &$_b) {
$r = (($rgb >> 16) & 0xFF);
$g = (($rgb >> 8) & 0xFF);
$b = (($rgb) & 0xFF);
$_r += $r;
$_g += $g;
$_b += $b;
$r /= 256;
$g /= 256;
$b /= 256;
// based on perceived brightness
$out = sqrt((0.241*$r*$r) + (0.691*$g*$g) + (0.068*$b*$b));
return $out;
}
function get_tty_width() {
$output = exec('tput cols');
return $output;
}
function get_ascii_pixel($img, $x0, $x1, $y0, $y1) {
global $HSV_COLORS, $PIXELS;
$total = 0;
$count = 0;
$r = $g = $b = 0;
for ($x = round($x0); $x < round($x1); $x++) {
for ($y = round($y0); $y < round($y1); $y++) {
$total += brightness_from_index(imagecolorat($img, $x, $y), $r, $g, $b);
$count++;
}
}
$r /= $count;
$g /= $count;
$b /= $count;
$hsv = rgb_to_hsv($r, $g, $b);
$hue = ($hsv[0] * 60) % 360;
$color_distances = array();
foreach ($HSV_COLORS as $_hue => $value) {
$color_distances[$value] []= abs($hue - $_hue);
$color_distances[$value] []= 360 - abs($hue - $_hue);
}
$color_distances = array_map('min', $color_distances);
asort($color_distances);
$nearest_hue = key($color_distances);
next($color_distances);
$next_nearest_hue = key($color_distances);
$next_nearest_distance = current($color_distances);
$lightness = $hsv[2];
if ($next_nearest_distance < 60 && $lightness < 0.8 && $lightness > 0.4) {
$back_hue = $nearest_hue;
$nearest_hue = $next_nearest_hue;
$saturation_glyph = (string)min(round($hsv[2] * $hsv[1] * 5) / 10 + 0.5, 0.9);
} else {
$back_hue = ($lightness > 0.6) ? "7" : "0";
$saturation_glyph = (string)min(round($hsv[2] * $hsv[1] * 10) / 10, 0.9);
}
$glyph = $PIXELS[$saturation_glyph];
return "\033[0;3" . $nearest_hue . ";4".$back_hue."m".$glyph;
}
for ($y = 0; $y < $target_height - 1; $y++) {
for ($x = 0; $x < $target_width - 1; $x++) {
echo get_ascii_pixel($img, $x * $ascii_x_scale, ($x+1) * $ascii_x_scale, $y * $ascii_y_scale, ($y + 1) * $ascii_y_scale);
}
echo "\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment