ASCII Art 文字藝術


在早期沒有圖形介面時,或者是在打字機上,若要顯示圖像,一種方式是透過 ASCII 可見字元的組合來呈現圖像內容,在圖形介面早就普及的現在,這種顯示圖像的方式被作為一種文字藝術,稱為 ASCII Art,例如:

ASCII Art 文字藝術

顯示 ASCII Art 時,建議使用等寬字型,並調整適當行距,上圖是從 100 x 100 的圖片產生,將上圖放大來看,可觀察到都是 ASCII 可見字元:

ASCII Art 文字藝術

要撰寫程式產生這樣的文字藝術並不難,最主要的,是將 ASCII 可見字元排列一下:

ASCII = '$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`\'. '

可以看到,左邊的字元複雜度比較高,右邊的字元複雜度比較低,若遠遠地看 ASCII Art 文字圖,就像個灰階圖片,這表示灰階值高的像素可以對應至右邊字元,灰階值低的像素可以對應至左邊字元,也就是說,實際上也不一定要用 ASCII 字元,只要依字元的複雜度排列,並對應灰階值就可以了。

就上面的 ASCII 來說,長度為 70,基本上就是將 0 到 255 共 256 個值,對應至 0 到 69 共 70 個值,如果要讓產生的圖的雜訊更少一些,可以稍微偏移一些,讓灰階值高的部份,儘量對應至右邊字元,例如下例的 UNIT 在計算時,偏移至 257:

ASCII = '$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`\'. '
UNIT = 257 / len(ASCII)
def to_ascii_art(gray):
    return ASCII[int(gray / UNIT)]

結合 NumPy 與 OpenCV 的話,就只要撰寫以下的程式:

import cv2
import numpy as np

ASCII = '$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`\'. '
UNIT = 257 / len(ASCII)
def to_ascii_art(gray):
    return ASCII[int(gray / UNIT)]
to_ascii_art = np.frompyfunc(to_ascii_art, 1, 1)

img = cv2.imread('caterpillar.jpg', cv2.IMREAD_GRAYSCALE)
ascii_art = to_ascii_art(img)

剩下的就是看你要將 ascii_art 輸出為什麼了,例如輸出為 .txt:

with open('caterpillar.txt', 'w') as f:
    for row in ascii_art:
        f.write(''.join(row) + '\n')

以 250 x 250 的圖片產生的文字內容,就會像是:

ASCII Art 文字藝術

當然,灰階度也不一定要來產生對應的字元,也可以是對應的大小圖案,例如圓:

import cv2
import numpy as np

img = cv2.imread('caterpillar_100x100.jpg', cv2.IMREAD_GRAYSCALE)
height, width = img.shape

max_r = 3
factor = max_r * 2

# 繪圖用
dots = np.zeros((height * factor, width * factor))
# 背景白色
dots.fill(255)

for y in range(height):
    for x in range(width):
        # 繪圓
        cv2.circle(
            dots,                                   # 目標圖片
            (x * factor, y * factor),               # 圓心
            int((255 - img[y, x]) / 255 * max_r) ,  # 半徑(幾個像素,也就是要整數)
            (0, 0, 0),                              # BGR
            -1                                      # 框線大小,-1 表示填滿
        )

cv2.imshow('Dots', dots)

cv2.waitKey(0)
cv2.destroyAllWindows()

這會產生以下的圖片:

ASCII Art 文字藝術