经过3晚上的研究,成功实现用php将图片转换成ascii编码图
主要原理:分析像素点的灰度值,用不同字符的深浅度表示(@和.),然后进行字符串组合,输出
一:以下是封装类
class ImgToASCII { protected $img_text; protected $img_data; // public $ascii\_str = '@#&$8RGA90Q27y=ro|i!;:"`-\_. ';//12个个字符 // protected $str = "~!@#$%^&*()_+1234567890-=qwertyuiop\[\]\\\asdfghjkl;'zxcvbnm,.QWERTYUIOP{}|ASDFGHJKL:\\"ZXCVBNM<>?"; public $ascii\_str = 'MWNQqpHAgpOU@#%ER804526tJD)"j\]\[sawf\\(x*7ll11\_\_++rr!!::;;~~^\\'\\',,,```...--- ';//字符 protected $config = array( 'img_path' => '', 'step_x' => 1, 'step_y' => 1, ); public function __construct($param=array()) { if(is_array($param)){ $this->config=array\_intersect\_key($param,$this->config); }else{ $this->config\['img_path'\]=$param; } var_dump($this->config); } /** * 将文件转为gd句柄 * @param null $img_path * @return resource * @throws Exception */ public function getImg($img_path = null) { $img\_path == null && $img\_path = $this->config\['img_path'\]; $arr = getimagesize($img_path); // var_dump($arr); if ($arr\[2\] == 1) { $this->img\_data = imagecreatefromgif($img\_path); } else if ($arr\[2\] == 2) { $this->img\_data = imagecreatefromjpeg($img\_path); } else if ($arr\[2\] == 3) { $this->img\_data = imagecreatefrompng($img\_path); } else { throw new \\Exception("对不起,暂不支持该格式!"); } return $this; } public function handleImg($img_data = null) { $img\_data==null && $img\_data=$this->img_data; $x\_y\_array = $this->getImgXY($img_data); $output = ""; for ($j = 0; $j < $x\_y\_array\['y'\]; $j += $this->config\['step_x'\]) { for ($i = 0; $i < $x\_y\_array\['x'\]; $i += $this->config\['step_x'\]) { $colors = imagecolorsforindex($img\_data, imagecolorat($img\_data, $i, $j)); //获取像素块的代表点RGB信息 $greyness = $this->countGray($colors\['red'\], $colors\["green"\], $colors\["blue"\]) / 255; //灰度值计算公式:Gray=R\*0.3+G\*0.59+B*0.11 $offset = (int)floor($greyness * (strlen($this->ascii_str) - 1)); //根据灰度值选择合适的字符 if ($offset == (strlen($this->ascii_str) - 1)) $output .= " "; //替换空格为 ;方便网页输出 else $output .= $this->ascii_str\[$offset\]; } $output .= "\\n"; } imagedestroy($img_data); return $output; // echo $output; } /** * 测试字符颜色深度 */ public function test() { $str = $this->ascii_str; // $str = "~!@#$%^&*()_+1234567890-=qwertyuiop\[\]\\\asdfghjkl;'zxcvbnm,.QWERTYUIOP{}|ASDFGHJKL:\\"ZXCVBNM<>?"; $str = str_split($str, 20); // var_dump($str);return; $output = ''; for ($i = 0; $i <= 50; $i++) { $str0=str_split($str\[0\]); foreach ($str0 as $value) { $output .= str_repeat($value, 50); } $output .= "\\n"; } $str1=str_split($str\[1\]); for ($i = 0; $i <= 50; $i++) { foreach ($str1 as $value) { $output .= str_repeat($value, 50); } $output .= "\\n"; } $str2=str_split($str\[2\]); for ($i = 0; $i <= 50; $i++) { foreach ($str2 as $value) { $output .= str_repeat($value, 50); } $output .= "\\n"; } $str3=str_split($str\[3\]); for ($i = 0; $i <= 50; $i++) { foreach ($str3 as $value) { $output .= str_repeat($value, 50); } $output .= "\\n"; } $str4=str_split($str\[4\]); for ($i = 0; $i <= 50; $i++) { foreach ($str4 as $value) { $output .= str_repeat($value, 50); } $output .= "\\n"; } file\_put\_contents('1.text',$output); // echo $output; } /** * 获取图片长宽值 * @param null $img_data * @return array */ public function getImgXY($img_data = null) { $img\_data == null && $img\_data = $this->img_data; $arr = array( 'x' => imagesx($img_data), 'y' => imagesy($img_data), ); return $arr; } /** * 计算灰度值 * @param $r * @param $g * @param $b * @return float|int */ public function countGray($r, $g, $b) { $gray = ($r * 299 + $g * 587 + $b * 114 + 500) / 1000; return $gray; } }
调用方法:
$img = new ImgToASCII('3.gif'); $output=$img->getImg()->handleImg(); echo $output;
可自行处理handleImg方法的换行,以及字符增加标签输出到html
也可直接保存为文件,使用notepad++打开缩小字符效果更佳
$img = new ImgToASCII(array( 'img_path'=>'4.jpg', 'step_x'=>4,//步进值,越小精度越高,1 'step_y'=>4 )); $output=$img->getImg()->handleImg(); file\_put\_contents('1.txt',$output);
二:将动态gif动态输出
gif图片动态输出原理为:
用php获取gif所有帧,
然后所有帧进行转换成ascii,
配合nginx清屏命令进行动态输出
需要用到gif获取所有帧的类:
<?php namespace GifFrameExtractor; /** * Extract the frames (and their duration) of a GIF * * @version 1.5 * @link https://github.com/Sybio/GifFrameExtractor * @author Sybio (Clément Guillemain / @Sybio01) * @license http://opensource.org/licenses/gpl-license.php GNU Public License * @copyright Clément Guillemain */ class GifFrameExtractor { // Properties ------ /** * @var resource */ private $gif; /** * @var array */ private $frames; /** * @var array */ private $frameDurations; /** * @var array */ private $frameImages; /** * @var array */ private $framePositions; /** * @var array */ private $frameDimensions; /** * @var integer * * (old: $this->index) */ private $frameNumber; /** * @var array * * (old: $this->imagedata) */ private $frameSources; /** * @var array * * (old: $this->fileHeader) */ private $fileHeader; /** * @var integer The reader pointer in the file source * * (old: $this->pointer) */ private $pointer; /** * @var integer */ private $gifMaxWidth; /** * @var integer */ private $gifMaxHeight; /** * @var integer */ private $totalDuration; /** * @var integer */ private $handle; /** * @var array * * (old: globaldata) */ private $globaldata; /** * @var array * * (old: orgvars) */ private $orgvars; // Methods ------ /** * Extract frames of a GIF * * @param string $filename GIF filename path * @param boolean $originalFrames Get original frames (with transparent background) * * @return array */ public function extract($filename, $originalFrames = false) { if (!self::isAnimatedGif($filename)) { throw new \\Exception('The GIF image you are trying to explode is not animated !'); } $this->reset(); $this->parseFramesInfo($filename); $prevImg = null; for ($i = 0; $i < count($this->frameSources); $i++) { $this->frames\[$i\] = array(); $this->frameDurations\[$i\] = $this->frames\[$i\]\['duration'\] = $this->frameSources\[$i\]\['delay_time'\]; $img = imagecreatefromstring($this->fileHeader\["gifheader"\].$this->frameSources\[$i\]\["graphicsextension"\].$this->frameSources\[$i\]\["imagedata"\].chr(0x3b)); if (!$originalFrames) { if ($i > 0) { $prevImg = $this->frames\[$i - 1\]\['image'\]; } else { $prevImg = $img; } $sprite = imagecreate($this->gifMaxWidth, $this->gifMaxHeight); imagesavealpha($sprite, true); $transparent = imagecolortransparent($prevImg); if ($transparent > -1 && imagecolorstotal($prevImg) > $transparent) { $actualTrans = imagecolorsforindex($prevImg, $transparent); imagecolortransparent($sprite, imagecolorallocate($sprite, $actualTrans\['red'\], $actualTrans\['green'\], $actualTrans\['blue'\])); } if ((int) $this->frameSources\[$i\]\['disposal_method'\] == 1 && $i > 0) { imagecopy($sprite, $prevImg, 0, 0, 0, 0, $this->gifMaxWidth, $this->gifMaxHeight); } imagecopyresampled($sprite, $img, $this->frameSources\[$i\]\["offset\_left"\], $this->frameSources\[$i\]\["offset\_top"\], 0, 0, $this->gifMaxWidth, $this->gifMaxHeight, $this->gifMaxWidth, $this->gifMaxHeight); $img = $sprite; } $this->frameImages\[$i\] = $this->frames\[$i\]\['image'\] = $img; } return $this->frames; } /** * Check if a GIF file at a path is animated or not * * @param string $filename GIF path */ public static function isAnimatedGif($filename) { if (!($fh = @fopen($filename, 'rb'))) { return false; } $count = 0; while (!feof($fh) && $count < 2) { $chunk = fread($fh, 1024 * 100); //read 100kb at a time $count += preg\_match\_all('#\\x00\\x21\\xF9\\x04.{4}\\x00(\\x2C|\\x21)#s', $chunk, $matches); } fclose($fh); return $count > 1; } // Internals ------ /** * Parse the frame informations contained in the GIF file * * @param string $filename GIF filename path */ private function parseFramesInfo($filename) { $this->openFile($filename); $this->parseGifHeader(); $this->parseGraphicsExtension(0); $this->getApplicationData(); $this->getApplicationData(); $this->getFrameString(0); $this->parseGraphicsExtension(1); $this->getCommentData(); $this->getApplicationData(); $this->getFrameString(1); while (!$this->checkByte(0x3b) && !$this->checkEOF()) { $this->getCommentData(1); $this->parseGraphicsExtension(2); $this->getFrameString(2); $this->getApplicationData(); } } /** * Parse the gif header (old: get\_gif\_header) */ private function parseGifHeader() { $this->pointerForward(10); if ($this->readBits(($mybyte = $this->readByteInt()), 0, 1) == 1) { $this->pointerForward(2); $this->pointerForward(pow(2, $this->readBits($mybyte, 5, 3) + 1) * 3); } else { $this->pointerForward(2); } $this->fileHeader\["gifheader"\] = $this->dataPart(0, $this->pointer); // Decoding $this->orgvars\["gifheader"\] = $this->fileHeader\["gifheader"\]; $this->orgvars\["background_color"\] = $this->orgvars\["gifheader"\]\[11\]; } /** * Parse the application data of the frames (old: get\_application\_data) */ private function getApplicationData() { $startdata = $this->readByte(2); if ($startdata == chr(0x21).chr(0xff)) { $start = $this->pointer - 2; $this->pointerForward($this->readByteInt()); $this->readDataStream($this->readByteInt()); $this->fileHeader\["applicationdata"\] = $this->dataPart($start, $this->pointer - $start); } else { $this->pointerRewind(2); } } /** * Parse the comment data of the frames (old: get\_comment\_data) */ private function getCommentData() { $startdata = $this->readByte(2); if ($startdata == chr(0x21).chr(0xfe)) { $start = $this->pointer - 2; $this->readDataStream($this->readByteInt()); $this->fileHeader\["commentdata"\] = $this->dataPart($start, $this->pointer - $start); } else { $this->pointerRewind(2); } } /** * Parse the graphic extension of the frames (old: get\_graphics\_extension) * * @param integer $type */ private function parseGraphicsExtension($type) { $startdata = $this->readByte(2); if ($startdata == chr(0x21).chr(0xf9)) { $start = $this->pointer - 2; $this->pointerForward($this->readByteInt()); $this->pointerForward(1); if ($type == 2) { $this->frameSources\[$this->frameNumber\]\["graphicsextension"\] = $this->dataPart($start, $this->pointer - $start); } elseif ($type == 1) { $this->orgvars\["hasgx\_type\_1"\] = 1; $this->globaldata\["graphicsextension"\] = $this->dataPart($start, $this->pointer - $start); } elseif ($type == 0) { $this->orgvars\["hasgx\_type\_0"\] = 1; $this->globaldata\["graphicsextension_0"\] = $this->dataPart($start, $this->pointer - $start); } } else { $this->pointerRewind(2); } } /** * Get the full frame string block (old: get\_image\_block) * * @param integer $type */ private function getFrameString($type) { if ($this->checkByte(0x2c)) { $start = $this->pointer; $this->pointerForward(9); if ($this->readBits(($mybyte = $this->readByteInt()), 0, 1) == 1) { $this->pointerForward(pow(2, $this->readBits($mybyte, 5, 3) + 1) * 3); } $this->pointerForward(1); $this->readDataStream($this->readByteInt()); $this->frameSources\[$this->frameNumber\]\["imagedata"\] = $this->dataPart($start, $this->pointer - $start); if ($type == 0) { $this->orgvars\["hasgx\_type\_0"\] = 0; if (isset($this->globaldata\["graphicsextension_0"\])) { $this->frameSources\[$this->frameNumber\]\["graphicsextension"\] = $this->globaldata\["graphicsextension_0"\]; } else { $this->frameSources\[$this->frameNumber\]\["graphicsextension"\] = null; } unset($this->globaldata\["graphicsextension_0"\]); } elseif ($type == 1) { if (isset($this->orgvars\["hasgx\_type\_1"\]) && $this->orgvars\["hasgx\_type\_1"\] == 1) { $this->orgvars\["hasgx\_type\_1"\] = 0; $this->frameSources\[$this->frameNumber\]\["graphicsextension"\] = $this->globaldata\["graphicsextension"\]; unset($this->globaldata\["graphicsextension"\]); } else { $this->orgvars\["hasgx\_type\_0"\] = 0; $this->frameSources\[$this->frameNumber\]\["graphicsextension"\] = $this->globaldata\["graphicsextension_0"\]; unset($this->globaldata\["graphicsextension_0"\]); } } $this->parseFrameData(); $this->frameNumber++; } } /** * Parse frame data string into an array (old: parse\_image\_data) */ private function parseFrameData() { $this->frameSources\[$this->frameNumber\]\["disposal_method"\] = $this->getImageDataBit("ext", 3, 3, 3); $this->frameSources\[$this->frameNumber\]\["user\_input\_flag"\] = $this->getImageDataBit("ext", 3, 6, 1); $this->frameSources\[$this->frameNumber\]\["transparent\_color\_flag"\] = $this->getImageDataBit("ext", 3, 7, 1); $this->frameSources\[$this->frameNumber\]\["delay_time"\] = $this->dualByteVal($this->getImageDataByte("ext", 4, 2)); $this->totalDuration += (int) $this->frameSources\[$this->frameNumber\]\["delay_time"\]; $this->frameSources\[$this->frameNumber\]\["transparent\_color\_index"\] = ord($this->getImageDataByte("ext", 6, 1)); $this->frameSources\[$this->frameNumber\]\["offset_left"\] = $this->dualByteVal($this->getImageDataByte("dat", 1, 2)); $this->frameSources\[$this->frameNumber\]\["offset_top"\] = $this->dualByteVal($this->getImageDataByte("dat", 3, 2)); $this->frameSources\[$this->frameNumber\]\["width"\] = $this->dualByteVal($this->getImageDataByte("dat", 5, 2)); $this->frameSources\[$this->frameNumber\]\["height"\] = $this->dualByteVal($this->getImageDataByte("dat", 7, 2)); $this->frameSources\[$this->frameNumber\]\["local\_color\_table_flag"\] = $this->getImageDataBit("dat", 9, 0, 1); $this->frameSources\[$this->frameNumber\]\["interlace_flag"\] = $this->getImageDataBit("dat", 9, 1, 1); $this->frameSources\[$this->frameNumber\]\["sort_flag"\] = $this->getImageDataBit("dat", 9, 2, 1); $this->frameSources\[$this->frameNumber\]\["color\_table\_size"\] = pow(2, $this->getImageDataBit("dat", 9, 5, 3) + 1) * 3; $this->frameSources\[$this->frameNumber\]\["color\_table"\] = substr($this->frameSources\[$this->frameNumber\]\["imagedata"\], 10, $this->frameSources\[$this->frameNumber\]\["color\_table_size"\]); $this->frameSources\[$this->frameNumber\]\["lzw\_code\_size"\] = ord($this->getImageDataByte("dat", 10, 1)); $this->framePositions\[$this->frameNumber\] = array( 'x' => $this->frameSources\[$this->frameNumber\]\["offset_left"\], 'y' => $this->frameSources\[$this->frameNumber\]\["offset_top"\], ); $this->frameDimensions\[$this->frameNumber\] = array( 'width' => $this->frameSources\[$this->frameNumber\]\["width"\], 'height' => $this->frameSources\[$this->frameNumber\]\["height"\], ); // Decoding $this->orgvars\[$this->frameNumber\]\["transparent\_color\_flag"\] = $this->frameSources\[$this->frameNumber\]\["transparent\_color\_flag"\]; $this->orgvars\[$this->frameNumber\]\["transparent\_color\_index"\] = $this->frameSources\[$this->frameNumber\]\["transparent\_color\_index"\]; $this->orgvars\[$this->frameNumber\]\["delay\_time"\] = $this->frameSources\[$this->frameNumber\]\["delay\_time"\]; $this->orgvars\[$this->frameNumber\]\["disposal\_method"\] = $this->frameSources\[$this->frameNumber\]\["disposal\_method"\]; $this->orgvars\[$this->frameNumber\]\["offset\_left"\] = $this->frameSources\[$this->frameNumber\]\["offset\_left"\]; $this->orgvars\[$this->frameNumber\]\["offset\_top"\] = $this->frameSources\[$this->frameNumber\]\["offset\_top"\]; // Updating the max width if ($this->gifMaxWidth < $this->frameSources\[$this->frameNumber\]\["width"\]) { $this->gifMaxWidth = $this->frameSources\[$this->frameNumber\]\["width"\]; } // Updating the max height if ($this->gifMaxHeight < $this->frameSources\[$this->frameNumber\]\["height"\]) { $this->gifMaxHeight = $this->frameSources\[$this->frameNumber\]\["height"\]; } } /** * Get the image data byte (old: get\_imagedata\_byte) * * @param string $type * @param integer $start * @param integer $length * * @return string */ private function getImageDataByte($type, $start, $length) { if ($type == "ext") { return substr($this->frameSources\[$this->frameNumber\]\["graphicsextension"\], $start, $length); } // "dat" return substr($this->frameSources\[$this->frameNumber\]\["imagedata"\], $start, $length); } /** * Get the image data bit (old: get\_imagedata\_bit) * * @param string $type * @param integer $byteIndex * @param integer $bitStart * @param integer $bitLength * * @return number */ private function getImageDataBit($type, $byteIndex, $bitStart, $bitLength) { if ($type == "ext") { return $this->readBits(ord(substr($this->frameSources\[$this->frameNumber\]\["graphicsextension"\], $byteIndex, 1)), $bitStart, $bitLength); } // "dat" return $this->readBits(ord(substr($this->frameSources\[$this->frameNumber\]\["imagedata"\], $byteIndex, 1)), $bitStart, $bitLength); } /** * Return the value of 2 ASCII chars (old: dualbyteval) * * @param string $s * * @return integer */ private function dualByteVal($s) { $i = ord($s\[1\]) * 256 + ord($s\[0\]); return $i; } /** * Read the data stream (old: read\_data\_stream) * * @param integer $firstLength */ private function readDataStream($firstLength) { $this->pointerForward($firstLength); $length = $this->readByteInt(); if ($length != 0) { while ($length != 0) { $this->pointerForward($length); $length = $this->readByteInt(); } } } /** * Open the gif file (old: loadfile) * * @param string $filename */ private function openFile($filename) { $this->handle = fopen($filename, "rb"); $this->pointer = 0; $imageSize = getimagesize($filename); $this->gifWidth = $imageSize\[0\]; $this->gifHeight = $imageSize\[1\]; } /** * Close the read gif file (old: closefile) */ private function closeFile() { fclose($this->handle); $this->handle = 0; } /** * Read the file from the beginning to $byteCount in binary (old: readbyte) * * @param integer $byteCount * * @return string */ private function readByte($byteCount) { $data = fread($this->handle, $byteCount); $this->pointer += $byteCount; return $data; } /** * Read a byte and return ASCII value (old: readbyte_int) * * @return integer */ private function readByteInt() { $data = fread($this->handle, 1); $this->pointer++; return ord($data); } /** * Convert a $byte to decimal (old: readbits) * * @param string $byte * @param integer $start * @param integer $length * * @return number */ private function readBits($byte, $start, $length) { $bin = str\_pad(decbin($byte), 8, "0", STR\_PAD_LEFT); $data = substr($bin, $start, $length); return bindec($data); } /** * Rewind the file pointer reader (old: p_rewind) * * @param integer $length */ private function pointerRewind($length) { $this->pointer -= $length; fseek($this->handle, $this->pointer); } /** * Forward the file pointer reader (old: p_forward) * * @param integer $length */ private function pointerForward($length) { $this->pointer += $length; fseek($this->handle, $this->pointer); } /** * Get a section of the data from $start to $start + $length (old: datapart) * * @param integer $start * @param integer $length * * @return string */ private function dataPart($start, $length) { fseek($this->handle, $start); $data = fread($this->handle, $length); fseek($this->handle, $this->pointer); return $data; } /** * Check if a character if a byte (old: checkbyte) * * @param integer $byte * * @return boolean */ private function checkByte($byte) { if (fgetc($this->handle) == chr($byte)) { fseek($this->handle, $this->pointer); return true; } fseek($this->handle, $this->pointer); return false; } /** * Check the end of the file (old: checkEOF) * * @return boolean */ private function checkEOF() { if (fgetc($this->handle) === false) { return true; } fseek($this->handle, $this->pointer); return false; } /** * Reset and clear this current object */ private function reset() { $this->gif = null; $this->totalDuration = $this->gifMaxHeight = $this->gifMaxWidth = $this->handle = $this->pointer = $this->frameNumber = 0; $this->frameDimensions = $this->framePositions = $this->frameImages = $this->frameDurations = $this->globaldata = $this->orgvars = $this->frames = $this->fileHeader = $this->frameSources = array(); } // Getter / Setter ------ /** * Get the total of all added frame duration * * @return integer */ public function getTotalDuration() { return $this->totalDuration; } /** * Get the number of extracted frames * * @return integer */ public function getFrameNumber() { return $this->frameNumber; } /** * Get the extracted frames (images and durations) * * @return array */ public function getFrames() { return $this->frames; } /** * Get the extracted frame positions * * @return array */ public function getFramePositions() { return $this->framePositions; } /** * Get the extracted frame dimensions * * @return array */ public function getFrameDimensions() { return $this->frameDimensions; } /** * Get the extracted frame images * * @return array */ public function getFrameImages() { return $this->frameImages; } /** * Get the extracted frame durations * * @return array */ public function getFrameDurations() { return $this->frameDurations; } }
调用以及输出代码:
$img = new ImgToASCII(); include_once 'GifFrameExtractor.php'; $gif = new \\GifFrameExtractor\\GifFrameExtractor(); $a =$gif->extract('2.gif'); $output_array = array(); foreach ($a as $key=> $value){ $path = "./a/{$key}.png"; imagepng($value\['image'\],$path); $output_array\[\] = $img->getImg($path)->handleImg(); } ob_start(); while(1){ foreach ($output_array as$output){ echo "\\033\[2J";//清屏 echo $output; usleep(100000);//延时,避免播放过快 } usleep(1000000);//循环播放 }