首頁 >後端開發 >PHP問題 >教你一招使用ASCII字元產生圖片

教你一招使用ASCII字元產生圖片

醉折花枝作酒筹
醉折花枝作酒筹轉載
2021-07-22 15:50:093347瀏覽

ASCII是基於拉丁字母的一套電腦編碼系統,主要用於顯示現代英語和其他西歐語言。其實ASCII字元不只可以作為資訊交換標準,還可以用它來產生圖片,今天我們就來介紹介紹。

教你一招使用ASCII字元產生圖片

網路上常有一些字元做成的圖片,例如這樣:

教你一招使用ASCII字元產生圖片

細想一下,這裡面主要運用到了幾個知識,有:

  • 從圖片解析出像素顏色(也就是通常說的RGB值)

  • 去色處理

  • 像素映射到字元

作為世界上最好的語言,用PHP實現有趣功能也是易如反掌。下面講解一下具體實作。

1. 解析圖片中的像素顏色

解析圖片中的像素顏色,我們需要了解圖片儲存的格式,這裡就以BMP圖片為例。什麼?網路上找不到BMP圖片?用QQ的截張圖,存成BMP就行了。

BMP圖片並不是從檔案的第1個位元組開始就是像素數據,而是一個個14位元組的檔案頭,保存著檔案的元資訊。緊鄰的是一個40位元組的圖片頭結構,保存圖片相關的元資料。

詳細列出文件頭和圖片頭結構的每個字段,就有些太無聊了(詳細的頭信息可以參見附錄)。想要解析圖片的像素,只需要這4 個資訊:

  • 圖片檔案的總體大小(檔案的第3~6個位元組)

  • #像素資料從檔案的哪裡開始(11~14個位元組)

  • #圖片的寬度和高度(寬:19~22位元組處、高:23 ~26位元組處)

  • 圖片的一個像素佔幾個位元組(29~30位元組)

想要解析出二進位中的數據,用unpack() 結合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. 獲得像素顏色以及去色

從上一節可以知道,我們想處理的圖片,像素資料從檔案的第54位元組開始,每個像素資料佔據24 bit。這24 bit中R(紅)、G(綠)、B(藍)的值各佔8 bit(1位元組)。

假如我想得到圖片第x 行,第y 列的RGB值,那麼對應的RGB值的位置應該這樣計算:

像素(x, y)的 B 值偏移 = 像素数据开始位置 + 3 * (图片宽度 * x + y)
像素(x, y)的 G 值偏移 = 像素数据开始位置 + 3 * (图片宽度 * x + y) + 1
像素(x, y)的 R 值偏移 = 像素数据开始位置 + 3 * (图片宽度 * x + y) + 2

從式子中發現三原色的值是以BGR順序排列的,不是通常的RGB順序。

如果你按照這個方法,將像素按順序一個個畫在一張畫布上,你會發現得到的圖片是顛倒的,這是因為圖片的像素資訊是倒過來儲存的,最左上角的像素實際上位於檔案的最末尾,所以想得到一張正過來的圖片,像素資料應該這麼取:

像素(x, y)的 B 值偏移 = 文件大小 - 3 * (图片宽度 * x + y) - 3
像素(x, y)的 G 值偏移 = 文件大小 - 3 * (图片宽度 * x + y) - 2
像素(x, y)的 R 值偏移 = 文件大小 - 3 * (图片宽度 * x + y) - 1

像素的顏色是取到了,但最終ascii圖是黑白的,怎麼進行去色呢?去色的演算法很多,這裡就用最簡單粗暴的:

新的R、G、B值 = [min(R, G, B) + max(R, G, B)] / 2;

黑白的像素,R、G、B都是一樣的值,這個值可以稱作像素的明亮度

最終,取像素的操作可以定義成一個函數:

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. 像素到字元映射

到了最後一個環節,我們要將圖片每個像素的深度轉換成ascii字元。 ascii字元本身沒有顏色深淺一說。但是如果你把”#”和”.”分別排列成100x100的正方形,從視覺上”#”會比”.”顏色更暗一點。

我們可以取若干個字元代表不同像素的明亮度,某個像素的明亮度處於某個區間時,就以對應等級的字元替換:

function getChar($colorValue) {
    $map = '@#mdohsy+/-:.` ';
    return $map[(int) ($colorValue / 18)];
}

還有一個問題:如果把每個像素都用一個字元替換的話,那麼輸出的字元圖將會非常巨大。所以最好是用一個字元取代原圖中 NxN 的像素區塊。整個像素區塊的明亮度,就取區塊中每個像素明亮度的平均值。

以上問題都解決了,最後拿一張蒙娜麗莎的微笑來測試:

教你一招使用ASCII字元產生圖片

教你一招使用ASCII字元產生圖片

效果還不錯:-)。如果終端背景是白色的,可以將表示明亮度的字元序列反過來:

// $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视频教程

以上是教你一招使用ASCII字元產生圖片的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除