Home > Article > Backend Development > Teach you a trick to generate images using ASCII characters
ASCII is a computer coding system based on the Latin alphabet, mainly used to display modern English and other Western European languages. In fact, ASCII characters can not only be used as an information exchange standard, but can also be used to generate images. Today we will introduce them.
There are often pictures made of characters on the Internet, such as this:
Think about it carefully, the main thing here is Several knowledges are used, including:
Parse the pixel color from the image (which is usually the RGB value)
Decolorization
Pixel mapping to characters
As the best language in the world, it is easy to implement interesting functions with PHP. The specific implementation is explained below.
To analyze the pixel color in the picture, we need to understand the format of the picture storage. Here we take the BMP picture as an example. What? Can't find BMP pictures online? Just use QQ to take a screenshot and save it as BMP.
BMP images do not have pixel data starting from the first byte of the file, but each 14-byte file header, which stores the meta-information of the file. Next to it is a 40-byte image header structure, which stores meta-information related to the image.
It would be a bit boring to list each field of the file header and image header structure in detail (see the appendix for detailed header information). To parse the pixels of an image, you only need these 4 pieces of information:
The overall size of the image file (the 3rd to 6th bytes of the file)
Where the pixel data starts in the file (11~14 bytes)
The width and height of the image (width: 19~22 bytes, height: 23 ~26 bytes)
How many bytes does one pixel of the picture occupy (29~30 bytes)
Want to parse To get the data in binary, you can use unpack() combined with substr():
$data = file_get_contents('image.bmp');$ret = unpack('v/Vsize/v/v/VpixelStart/V/Vwidth/Vheight/v/vbytePerPixel/V*6', substr($data, 0x0, 54));/** * $ret的内容: * array ( * 'size' => 706554, * 'pixelStart' => 54, * 'width' => 500, * 'height' => 471, * 'bytePerPixel' => 24, * ); */
As you can know from the previous section, what we want to process For pictures, pixel data starts from the 54th byte of the file, and each pixel data occupies 24 bits. Among these 24 bits, the values of R (red), G (green), and B (blue) each occupy 8 bits (1 byte).
If I want to get the RGB value of row x and column y of the picture, then the position of the corresponding RGB value should be calculated like this:
像素(x, y)的 B 值偏移 = 像素数据开始位置 + 3 * (图片宽度 * x + y) 像素(x, y)的 G 值偏移 = 像素数据开始位置 + 3 * (图片宽度 * x + y) + 1 像素(x, y)的 R 值偏移 = 像素数据开始位置 + 3 * (图片宽度 * x + y) + 2
It is found from the formula that the value of the three primary colors is The BGR order is not the usual RGB order.
If you follow this method and draw the pixels one by one on a canvas in order, you will find that the resulting picture is upside down. This is because the pixel information of the picture is stored upside down. The pixels in the upper left corner are actually at the end of the file, so if you want to get a straight-forward picture, the pixel data should be obtained like this:
像素(x, y)的 B 值偏移 = 文件大小 - 3 * (图片宽度 * x + y) - 3 像素(x, y)的 G 值偏移 = 文件大小 - 3 * (图片宽度 * x + y) - 2 像素(x, y)的 R 值偏移 = 文件大小 - 3 * (图片宽度 * x + y) - 1
The color of the pixels is obtained, but the final ASCII image is black and white. How to proceed? What about color removal? There are many algorithms for color removal, here is the simplest and crudest one:
新的R、G、B值 = [min(R, G, B) + max(R, G, B)] / 2;
For black and white pixels, R, G, and B all have the same value. This value can be called the brightness of the pixel
Finally, the operation of taking pixels can be defined as a function:
function getPixelColor($x, $y) { global $width, $size, $data; $b = ord($data[$size - 3 * ($width * $x + $y) - 3]); $g = ord($data[$size - 3 * ($width * $x + $y) - 2]); $r = ord($data[$size - 3 * ($width * $x + $y) - 1]); return (min($r, $g, $b) + max($r, $g, $b)) >> 1; }
In the last step, we have to convert the depth of each pixel of the image into ascii character. ASCII characters themselves have no color shades. But if you arrange "#" and "." into 100x100 squares, visually "#" will be darker than ".".
We can take several characters to represent the brightness of different pixels. When the brightness of a certain pixel is in a certain range, replace it with the corresponding level of characters:
function getChar($colorValue) { $map = '@#mdohsy+/-:.` '; return $map[(int) ($colorValue / 18)]; }
There is another question : If each pixel is replaced with a character, the output character image will be very huge. So it is best to use a character to replace the NxN pixel blocks in the original image. The brightness of the entire pixel block is the average brightness of each pixel in the block.
The above problems have been solved. Finally, I took a Mona Lisa smile to test:
The effect is pretty good :-). If the terminal background is white, the character sequence representing brightness can be reversed:
// $map = '@#mdohsy+/-:.` '; $map = ' `.:-/+yshodm#@'; // 反过来
<?php $data = file_get_contents('timg.bmp');$ret = unpack('v/Vsize/v/v/VpixelStart/V/Vwidth/Vheight/v/vbytePerPixel/V*6', substr($data, 0x0, 54));$size = $ret['size'];$offset = $ret['pixelStart'];$width = $ret['width'];$height = $ret['height'];$bitDepth = $ret['bytePerPixel'];$pixelLenPerChar = 4;$charImgWidth = (int) ($width / $pixelLenPerChar);$charImgHeight = (int) ($height / $pixelLenPerChar);for ($i = 0; $i !== $charImgHeight; $i++) { $buf = ''; for ($j = 0; $j !== $charImgWidth; $j++) { $sum = 0; for ($k = 0; $k !== $pixelLenPerChar; $k++) { for ($l = 0; $l !== $pixelLenPerChar; $l++) { $sum += getPixelColor($pixelLenPerChar * $i + $k, $pixelLenPerChar * $j + $l); } } $sum = (int) ($sum / $pixelLenPerChar / $pixelLenPerChar); $buf = getChar($sum) . $buf; } echo $buf . PHP_EOL; }function getPixelColor($x, $y) { global $width, $size, $data; $b = ord($data[$size - 3 * ($width * $x + $y) - 3]); $g = ord($data[$size - 3 * ($width * $x + $y) - 2]); $r = ord($data[$size - 3 * ($width * $x + $y) - 1]); return (min($r, $g, $b) + max($r, $g, $b)) >> 1; }function getChar($colorValue) { $map = '@#mdohsy+/-:.` '; return $map[(int) ($colorValue / 18)]; }
偏移 | 大小(字节) | 含义 | 本文中图片示例值 |
---|---|---|---|
0 | 2 | 固定为”BM”两个字符的编码 | 0x42 0x4d |
2 | 4 | 文件大小 | 0x000ac7fa |
6 | 4 | 保留字段,一般为 0 | 0x00000000 |
10 | 4 | 像素数据起始处偏移 | 0x00000036 |
偏移 | 大小(字节) | 含义 | 本文中图片示例值 |
---|---|---|---|
14 | 4 | 图片头的大小(字节) | 0x00000028 |
18 | 4 | 图片的宽度 | 0x000001f4 |
22 | 4 | 图片的高度 | 0x000001d7 |
26 | 2 | 图像的帧数(静态图都是1) | 0x0001 |
28 | 2 | 一个像素占的比特位数 | 0x0018 |
30 | 4 | 保留字段,一般为 0 | 0x000000 |
34 | 4 | 像素数据占用的总字节数 | 0x000ac7c4 |
38 | 4 | 保留字段,一般为 0 | 0x000000 |
42 | 4 | 保留字段,一般为 0 | 0x000000 |
46 | 4 | 保留字段,一般为 0 | 0x000000 |
50 | 4 | 保留字段,一般为 0 | 0x000000 |
推荐学习:php视频教程
The above is the detailed content of Teach you a trick to generate images using ASCII characters. For more information, please follow other related articles on the PHP Chinese website!