Home  >  Article  >  Backend Development  >  Teach you a trick to generate images using ASCII characters

Teach you a trick to generate images using ASCII characters

醉折花枝作酒筹
醉折花枝作酒筹forward
2021-07-22 15:50:093260browse

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.

Teach you a trick to generate images using ASCII characters

There are often pictures made of characters on the Internet, such as this:

Teach you a trick to generate images using ASCII characters

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.

1. Analyze the pixel color in the picture

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,
 * );
 */

2. Obtain pixel color and remove color

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;
}

3. Pixel to character mapping

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:

Teach you a trick to generate images using ASCII characters

Teach you a trick to generate images using ASCII characters

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(&#39;timg.bmp&#39;);$ret = unpack(&#39;v/Vsize/v/v/VpixelStart/V/Vwidth/Vheight/v/vbytePerPixel/V*6&#39;, substr($data, 0x0, 54));$size = $ret[&#39;size&#39;];$offset = $ret[&#39;pixelStart&#39;];$width = $ret[&#39;width&#39;];$height = $ret[&#39;height&#39;];$bitDepth = $ret[&#39;bytePerPixel&#39;];$pixelLenPerChar = 4;$charImgWidth = (int) ($width / $pixelLenPerChar);$charImgHeight = (int) ($height / $pixelLenPerChar);for ($i = 0; $i !== $charImgHeight; $i++) {    $buf = &#39;&#39;;    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)];
}

BMP文件头格式

偏移 大小(字节) 含义 本文中图片示例值
0 2 固定为”BM”两个字符的编码 0x42 0x4d
2 4 文件大小 0x000ac7fa
6 4 保留字段,一般为 0 0x00000000
10 4 像素数据起始处偏移 0x00000036

BMP图片头格式

偏移 大小(字节) 含义 本文中图片示例值
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!

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete