Skip to content

Instantly share code, notes, and snippets.

@alex-phillips
Created October 3, 2018 12:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alex-phillips/1836029fcd0f6cb7c483f62c88fc9136 to your computer and use it in GitHub Desktop.
Save alex-phillips/1836029fcd0f6cb7c483f62c88fc9136 to your computer and use it in GitHub Desktop.
<?php
$converter = new img2ansi();
$converter->run();
class img2ansi
{
private $_image;
private $_colors_array = array(
"black" => array(0, 0, 0),
"red" => array(205, 0, 0),
"green" => array(0, 205, 0),
"yellow" => array(205, 205, 0),
"blue" => array(0, 0, 238),
"magenta" => array(205, 0, 205),
"cyan" => array(0, 205, 205),
"gray" => array(229, 229, 229),
"dark gray" => array(127, 127, 127),
"bright red" => array(255, 0, 0),
"bright green" => array(0, 255, 0),
"bright yellow" => array(255, 255, 0),
"bright blue" => array(92, 92, 255),
"bright magenta" => array(255, 0, 255),
"bright cyan" => array(0, 255, 255),
"white" => array(255, 255, 255),
);
private $ansi_array = array(
"reset" => '0', # reset
"black" => '00;30', # black
"red" => '00;31', # red
"green" => '00;32', # green
"yellow" => '00;33', # yellow
"blue" => '00;34', # blue
"magenta" => '00;35', # magenta
"cyan" => '00;36', # cyan
"gray" => '00;37', # gray
"dark gray" => '01;30', # dark gray
"bright red" => '01;31', # bright red
"bright green" => '01;32', # bright green
"bright yellow" => '01;33', # bright yellow
"bright blue" => '01;34', # bright blue
"bright magenta" => '01;35', # bright magenta
"bright cyan" => '01;36', # bright cyan
"white" => '01;37', # white
);
public function __construct()
{
$args = getopt('i:');
if (isset($args['i'])) {
$this->_image = $args['i'];
}
else {
print "\nUsage:\n -i input file path to file for conversion\n\n";
exit;
}
}
public function run()
{
$this->_image = file_get_contents($this->_image);
$img = imagecreatefromstring($this->_image);
$width = imagesx($img);
$height = imagesy($img);
$fill_char = "##";
$output = '';
$last = '';
for($h = 0; $h < $height; $h++){
for($w = 0; $w < $width; $w++){
$rgb = imagecolorat($img, $w, $h);
$vals = imagecolorsforindex($img, $rgb);
$r = $vals['red'];
$g = $vals['green'];
$b = $vals['blue'];
$a = $vals['alpha'];
if ($w == $width/2) {
$var = 'test';
}
$color = $this->find_closest_color($r, $g, $b);
if($w == $width - 1){
$output .= "\n";
}
// else if ($a == 0) {
// $output .= " " * sizeof($fill_char);
// }
else {
$output .= $this->get_colored_string($fill_char, $color);
}
}
}
$output .= $this->get_colored_string('', 'reset');
$output .= "\n\n";
echo $output;
}
// Returns colored string
private function get_colored_string($string, $color = null) {
$colored_string = "";
if (isset($this->ansi_array[$color])) {
$colored_string .= "\033[" . $this->ansi_array[$color] . "m";
}
// Add string and end coloring
$colored_string .= $string . "\033[0m";
return $colored_string;
}
// private function find_closest_color($r, $g, $b)
// {
// $closest_distance = null;
// $match = '';
// foreach ($this->_colors_array as $name => $color) {
// $distance = sqrt(($color[0]-$r)^2+($color[1]-$g)^2+($color[2]-$b)^2);
// if ($closest_distance === null || $distance < $closest_distance) {
// $closest_distance = $distance;
// $match = $name;
// }
// }
// return $match;
// }
// private function find_closest_color($r, $g, $b){
// $differencearray = array();
// $names = array();
// foreach ($this->_colors_array as $name => $value) {
// $names[] = $name;
// $pow = pow($r-$value[0],2)+pow($g-$value[1],2)+pow($b-$value[2],2);
// $difference = sqrt(abs($pow));
// if ($pow < 0) {
// $difference = $difference * -1;
// }
// $differencearray[] = $difference;
// }
// $smallest = min($differencearray);
// $key = array_search($smallest, $differencearray);
// return $names[$key];
// }
private function find_closest_color($r, $g, $b)
{
return $this->getNearestColor(array($r, $g, $b), $this->_colors_array);
$diff = new color_difference();
$names = array();
$diffs = array();
foreach ($this->_colors_array as $name => $vals) {
$names[] = $name;
$diffs[] = $diff->deltaECIE2000(array($r, $g, $b), $vals);
}
$smallest = min($diffs);
$key = array_search($smallest, $diffs);
return $names[$key];
}
/*
* Returns the index of the palette-color which is most similar
* to $givenColor.
*
* $givenColor and the colors in $palette should be either
* formatted as (#)rrggbb
* (e. g. "ff0000", "4da4f3" or "#b5d7f3")
* or arrays with values for red, green and blue
* (e. g. $givenColor = array( 0xff, 0x00, 0x00 ) )
*
* References:
* function rgb2lab
* - http://www.f4.fhtw-berlin.de/~barthel/ImageJ/ImageJ.htm
*
* function rgb2lab & function deltaE
* - http://www.brucelindbloom.com
*/
private function getNearestColor(
$givenColor,
$palette = array( 'blue' => '43aafd','red' => 'fe6256','green' => '64b949','yellow' => 'fcf357',
'black' => '656565','white' => 'fdfdfd','orange' => 'fea800','purple' => '9773fe')
) {
if(!function_exists('rgb2lab')) {
function rgb2lab($rgb) {
$eps = 216/24389; $k = 24389/27;
// reference white D50
$xr = 0.964221; $yr = 1.0; $zr = 0.825211;
$rgb[0] = $rgb[0]/255; //R 0..1
$rgb[1] = $rgb[1]/255; //G 0..1
$rgb[2] = $rgb[2]/255; //B 0..1
// assuming sRGB (D65)
$rgb[0] = ($rgb[0] <= 0.04045)?($rgb[0]/12.92):pow(($rgb[0]+0.055)/1.055,2.4);
$rgb[1] = ($rgb[1] <= 0.04045)?($rgb[1]/12.92):pow(($rgb[1]+0.055)/1.055,2.4);
$rgb[2] = ($rgb[2] <= 0.04045)?($rgb[2]/12.92):pow(($rgb[2]+0.055)/1.055,2.4);
// sRGB D50
$x = 0.4360747*$rgb[0] + 0.3850649*$rgb[1] + 0.1430804 *$rgb[2];
$y = 0.2225045*$rgb[0] + 0.7168786*$rgb[1] + 0.0606169 *$rgb[2];
$z = 0.0139322*$rgb[0] + 0.0971045*$rgb[1] + 0.7141733 *$rgb[2];
$xr = $x/$xr; $yr = $y/$yr; $zr = $z/$zr;
$fx = ($xr > $eps)?pow($xr, 1/3):($fx = ($k * $xr + 16) / 116);
$fy = ($yr > $eps)?pow($yr, 1/3):($fy = ($k * $yr + 16) / 116);
$fz = ($zr > $eps)?pow($zr, 1/3):($fz = ($k * $zr + 16) / 116);
$lab = array();
$lab[] = round(( 116 * $fy ) - 16);
$lab[] = round(500*($fx-$fy));
$lab[] = round(200*($fy-$fz));
return $lab;
}
}
if(!function_exists('deltaE')) {
function deltaE($lab1, $lab2) {
// CMC 1:1
$l = 1; $c = 1;
$c1 = sqrt($lab1[1]*$lab1[1]+$lab1[2]*$lab1[2]);
$c2 = sqrt($lab2[1]*$lab2[1]+$lab2[2]*$lab2[2]);
$h1 = (((180000000/M_PI) * atan2($lab1[1],$lab1[2]) + 360000000) % 360000000)/1000000;
$t = (164 <= $h1 AND $h1 <= 345)?(0.56 + abs(0.2 * cos($h1+168))):(0.36 + abs(0.4 * cos($h1+35)));
$f = sqrt(pow($c1,4)/(pow($c1,4) + 1900));
$sl = ($lab1[0] < 16)?(0.511):((0.040975*$lab1[0])/(1 + 0.01765*$lab1[0]));
$sc = (0.0638 * $c1)/(1 + 0.0131 * $c1) + 0.638;
$sh = $sc * ($f * $t + 1 -$f);
return sqrt(
pow(($lab1[0]-$lab2[0])/($l * $sl),2) +
pow(($c1-$c2)/($c * $sc),2) +
pow(sqrt(
($lab1[1]-$lab2[1])*($lab1[1]-$lab2[1]) +
($lab1[2]-$lab2[2])*($lab1[2]-$lab2[2]) +
($c1-$c2)*($c1-$c2)
)/$sh,2)
);
}
}
if(!function_exists('str2rgb')) {
function str2rgb($str)
{
$str = preg_replace('~[^0-9a-f]~','',$str);
$rgb = str_split($str,2);
for($i=0;$i<3;$i++)
$rgb[$i] = intval($rgb[$i],16);
return $rgb;
}
}
$givenColorRGB = is_array($givenColor)?$givenColor:str2rgb($givenColor);
$min = 0xffff;
$return = NULL;
foreach($palette as $key => $color) {
$color = is_array($color)?$color:str2rgb($color);
if($min >= ($deltaE = deltaE(rgb2lab($color),rgb2lab($givenColorRGB))))
{
$min = $deltaE;
$return = $key;
}
}
return $return;
}
}
class color_difference {
public function deltaECIE2000 ($rgb1, $rgb2) {
list($l1, $a1, $b1) = $this->_rgb2lab($rgb1);
list($l2, $a2, $b2) = $this->_rgb2lab($rgb2);
$avg_lp = ($l1 + $l2) / 2;
$c1 = sqrt(pow($a1, 2) + pow($b1, 2));
$c2 = sqrt(pow($a2, 2) + pow($b2, 2));
$avg_c = ($c1 + $c2) / 2;
$g = (1 - sqrt(pow($avg_c , 7) / (pow($avg_c, 7) + pow(25, 7)))) / 2;
$a1p = $a1 * (1 + $g);
$a2p = $a2 * (1 + $g);
$c1p = sqrt(pow($a1p, 2) + pow($b1, 2));
$c2p = sqrt(pow($a2p, 2) + pow($b2, 2));
$avg_cp = ($c1p + $c2p) / 2;
$h1p = rad2deg(atan2($b1, $a1p));
if ($h1p < 0) {
$h1p += 360;
}
$h2p = rad2deg(atan2($b2, $a2p));
if ($h2p < 0) {
$h2p += 360;
}
$avg_hp = abs($h1p - $h2p) > 180 ? ($h1p + $h2p + 360) / 2 : ($h1p + $h2p) / 2;
$t = 1 - 0.17 * cos(deg2rad($avg_hp - 30)) + 0.24 * cos(deg2rad(2 * $avg_hp)) + 0.32 * cos(deg2rad(3 * $avg_hp + 6)) - 0.2 * cos(deg2rad(4 * $avg_hp - 63));
$delta_hp = $h2p - $h1p;
if (abs($delta_hp) > 180) {
if ($h2p <= $h1p) {
$delta_hp += 360;
}
else {
$delta_hp -= 360;
}
}
$delta_lp = $l2 - $l1;
$delta_cp = $c2p - $c1p;
$delta_hp = 2 * sqrt($c1p * $c2p) * sin(deg2rad($delta_hp) / 2);
$s_l = 1 + ((0.015 * pow($avg_lp - 50, 2)) / sqrt(20 + pow($avg_lp - 50, 2)));
$s_c = 1 + 0.045 * $avg_cp;
$s_h = 1 + 0.015 * $avg_cp * $t;
$delta_ro = 30 * exp(-(pow(($avg_hp - 275) / 25, 2)));
$r_c = 2 * sqrt(pow($avg_cp, 7) / (pow($avg_cp, 7) + pow(25, 7)));
$r_t = -$r_c * sin(2 * deg2rad($delta_ro));
$kl = $kc = $kh = 1;
$delta_e = sqrt(pow($delta_lp / ($s_l * $kl), 2) + pow($delta_cp / ($s_c * $kc), 2) + pow($delta_hp / ($s_h * $kh), 2) + $r_t * ($delta_cp / ($s_c * $kc)) * ($delta_hp / ($s_h * $kh)));
return $delta_e;
}
private function _rgb2lab ($rgb) {
return $this->_xyz2lab($this->_rgb2xyz($rgb));
}
private function _rgb2xyz ($rgb) {
list($r, $g, $b) = $rgb;
$r = $r <= 0.04045 ? $r / 12.92 : pow(($r + 0.055) / 1.055, 2.4);
$g = $g <= 0.04045 ? $g / 12.92 : pow(($g + 0.055) / 1.055, 2.4);
$b = $b <= 0.04045 ? $b / 12.92 : pow(($b + 0.055) / 1.055, 2.4);
$r *= 100;
$g *= 100;
$b *= 100;
$x = $r * 0.412453 + $g * 0.357580 + $b * 0.180423;
$y = $r * 0.212671 + $g * 0.715160 + $b * 0.072169;
$z = $r * 0.019334 + $g * 0.119193 + $b * 0.950227;
return array($x, $y, $z);
}
private function _xyz2lab ($xyz) {
list ($x, $y, $z) = $xyz;
$x /= 95.047;
$y /= 100;
$z /= 108.883;
$x = $x > 0.008856 ? pow($x, 1 / 3) : $x * 7.787 + 16 / 116;
$y = $y > 0.008856 ? pow($y, 1 / 3) : $y * 7.787 + 16 / 116;
$z = $z > 0.008856 ? pow($z, 1 / 3) : $z * 7.787 + 16 / 116;
$l = $y * 116 - 16;
$a = ($x - $y) * 500;
$b = ($y - $z) * 200;
return array($l, $a, $b);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment