Python 將圖片轉(zhuǎn)為字符畫

字符畫是一系列字符的組合,我們可以把字符看作是比較大塊的像素,一個字符能表現(xiàn)一種顏色(暫且這么理解吧),字符的種類越多,可以表現(xiàn)的顏色也越多,圖片也會更有層次感。

問題來了,像素是有顏色深淺的,我們?nèi)绾螌в胁煌伾南袼鼐幋a為對應的字符呢?我們是要轉(zhuǎn)換一張彩色的圖片,這么多的顏色,要怎么對應到單色的字符畫上去?

轉(zhuǎn)化方法

轉(zhuǎn)化的整體思路就是:RGB->灰度->字符,詳細來說就是:

  1. 將彩色圖片轉(zhuǎn)化為灰度圖,根據(jù)顏色深淺的 RGB 值映射為灰度值;
  2. 根據(jù)字符集順序及字符集長度,將灰度值映射到字符。

這里就要介紹灰度值和 RGB 的概念了。

  • RGB

RGB 色彩模式是通過對紅(R)、綠(G)、藍(B) 三個顏色通道的變化以及它們相互之間的疊加來得到各式各樣的顏色的,RGB 即是代表紅、綠、藍三個通道的顏色,這個標準幾乎包括了人類視力所能感知的所有顏色。
通常情況下,RGB 各有 256 級亮度,用數(shù)字表示為從 0、1、2... 到 255。

  • 灰度圖

灰度值:指黑白圖像中點的顏色深度,范圍一般從 0 到 255,白色為 255,黑色為 0,故黑白圖片也稱灰度圖像

那么如何將彩色圖片轉(zhuǎn)為灰度呢?

常用的公式如下,可以將像素的 RGB 值映射到灰度值:

gray = 0.2126 * r + 0.7152 * g + 0.0722 * b

得到灰度值后,就可以將灰度值和字符進行映射了??梢詣?chuàng)建一個不重復的字符列表,灰度值?。ò担┑挠昧斜黹_頭的符號,灰度值大(亮)的用列表末尾的符號。

下面是我們的字符畫所使用的字符集,一共有 70 個字符,從左住右, 表示的顏色深度依次遞減。字符的種類與數(shù)量可以自己根據(jù)字符畫的效果反復調(diào)試。

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

實現(xiàn)

字符集容量為 70,一個字符對應的值:區(qū)間寬度= 256/字符集長度,即區(qū)間寬度為 256/70 = 3.6。

gray 區(qū)間和字符的對應關系

[0.0, 3.6)      -->  $
[3.6,  7.2)      -->  @
[7.2,  10.8)     -->  B
...
[244.2, 247.8]   -->  '
[247.8, 251.4]   -->  .
[251.4, 255.0]   -->  

RGB 轉(zhuǎn)字符的函數(shù)

# 將256 灰度映射到 70 個字符上
def get_char(r, g, b, alpha=256):
    if alpha == 0:
        return " "
    length = len(ascii_char)
    gray = int(0.2126*r + 0.7152*g + 0.0722*b)

    # 每個字符對應的 gray 值區(qū)間寬度
    unit = (256.0+1)/length

    # gray值對應到 char_string 中的位置(索引值)
    index = int(gray/unit)
    return ascii_char[index]

完整代碼

import argparse

from PIL import Image

# 命令行輸入?yún)?shù)處理
parser = argparse.ArgumentParser()

parser.add_argument('file')     # 輸入圖片
parser.add_argument('-o', '--output')   # 輸出文件
parser.add_argument('--width', type=int, default=40)    # 輸出字符畫寬度
parser.add_argument('--height', type=int, default=40)   # 輸出字符畫高度

# 獲取參數(shù)
args = parser.parse_args()

IMG = args.file
WIDTH = args.width
HEIGHT = args.height
OUTPUT = args.output

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

# 將256 灰度映射到 70 個字符上
def get_char(r, g, b, alpha=256):
    if alpha == 0:
        return " "
    length = len(ascii_char)
    gray = int(0.2126*r + 0.7152*g + 0.0722*b)

    # 每個字符對應的 gray 值區(qū)間寬度
    unit = (256.0+1)/length

    # gray值對應到 char_string 中的位置(索引值)
    index = int(gray/unit)
    return ascii_char[index]


if __name__ == "__main__":

    im = Image.open(IMG)
    im = im.resize((WIDTH, HEIGHT), Image.NEAREST)

    txt = ""

    for i in range(HEIGHT):
        for j in range(WIDTH):
            txt += get_char(*im.getpixel((j, i)))
        txt += '\n'

    print(txt)

    if OUTPUT:
        with open(OUTPUT, 'w') as f:
            f.write(txt)
    else:
        with open('output.txt', 'w') as f:
            f.write(txt)

運行結果

  • 原圖
dora
dora
$ python ascii.py  ascii_dora.png --width=80 --height=40

                                 $$$#kqqq#$$
                           $kxxxxxx(    Ox    "Lq$
                        @xxxxxxxxmf      z      dxx0$
                     @xxxxxxxxxxx#    n  I}z  ( (xxxx0
                   $xxxxxxxxxxc#(Ip      ( COC( h    pu$
                  @xxxxxxxxxW      #    C *j :kM     "Opn-p
                 kxxxxxxxu>   "n#n-       Mkkkkk f-         $
                8xxxxxxxh                  }B&C    "-(nnnn- p
               $xxxxxxmn   I-}nnnOpphpz}     h              $
               oxxxxxu                       }n     }OpnI f
               Jxxxxx#    "kkkkkkka8%*aW%pn- -n        }CI$
               Yxxxxx-    nkkkkkkkkkkkkkkkkkkkkkkkkkkk*   $
               mxxxxx      kkkkkkkkkkkkkkkkkkkkkkkkkko   >
               $xxxxx      zkk8bYfcOMMkkkkkkkkkkkkkk8   >$
                cxxxxC      nfffffffffffc#kkkkkkkkkn    $
                $xxxxx"       Qfffffffffffukkkkkk8     $
                 @xxxxk}       hfffffffffffpkkka>     $
                  $xxxxx(        "nQffffffff8}      -$
                   $uxxxxW           -Cpp$%%%%%%%pn$
                     $xxxxc#8akkkkkkn      >b_ub0o&W$
                      $8kkkWdxxxxxx#        pIII[I0 $
                        xxxxxxxxxxxY        0IIIXIf n
                        qxxxxxxxxxxxd      h aJua"
                        $xxxxxxxx#k}  (hC-"        Ih$  $*(-C$
                         xx0qcxxxxq  n              n$-        $
                         qxxxxxxxx#  (              #          p
                         $xxxxxxxxc   "-          (I           p
                   $kkkk%$xxxxxxxxxJ      -npnn-  fh           $
                   $kkkk8$xxxxxxxxxxxC          "dxn          C
                         #xxxxxxxxxxxxxxdhnCW0uxxxx#         O
                         xxxxxxxxxxxxxxxxxxxxxuqB$$ 8      I$
                      >  xxxxxxxxxxxxxxxL$
                     (   pxxxxxxxxxxY$
                          Cxxxxxxuk$
                             >OO$
                     $           $
                      $C
                         $$Onn*$

:stuck_out_tongue_winking_eye:

知識點

命令行解析模塊 argparse

看下樣例就可以:

# 命令行輸入?yún)?shù)處理
parser = argparse.ArgumentParser()

parser.add_argument('file', help='the input file')     # 輸入圖片
parser.add_argument('-o', '--output', help='the output text file')   # 輸出文件
parser.add_argument('-w', '--width', type=int, default=40,
                    help='the width of the output, default is 40')    # 輸出字符畫寬度
parser.add_argument('--height', type=int, default=40,
                    help='the height of the output, default is 40')   # 輸出字符畫高度

# 獲取參數(shù)
args = parser.parse_args()

IMG = args.file
WIDTH = args.width
HEIGHT = args.height
OUTPUT = args.output

使用:

$ python ascii.py  --help
usage: ascii.py [-h] [-o OUTPUT] [-w WIDTH] [--height HEIGHT] file

positional arguments:
  file                  the input file

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        the output text file
  -w WIDTH, --width WIDTH
                        the width of the output, default is 40
  --height HEIGHT       the height of the output, default is 40

如果覺得有用,歡迎關注我的微信,有問題可以直接交流:

你的關注是對我最大的鼓勵!
你的關注是對我最大的鼓勵!
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容