Python 圖片轉字符畫
一、課程介紹
1. 課程來源
原創
2. 內容簡介
本課程講述怎樣使用 Python 將圖片轉為字符畫
3. 前置課程
4. 課程知識點
本節實驗中我們將實踐以下知識:
- Linux 命令行操作
- Python 基礎
- pillow 庫的使用
- argparse 庫的使用(參考教程)
二、實驗原理
字符畫是一系列字符的組合,我們可以把字符看作是比較大塊的像素,一個字符能表現一種顏色(暫且這么理解吧),字符的種類越多,可以表現的顏色也越多,圖片也會更有層次感。
問題來了,我們是要轉換一張彩色的圖片,這么這么多的顏色,要怎么對應到單色的字符畫上去?這里就要介紹灰度值的概念了。
灰度值:指黑白圖像中點的顏色深度,范圍一般從0到255,白色為255,黑色為0,故黑白圖片也稱灰度圖像
我們可以使用灰度值公式將像素的 RGB 值映射到灰度值:
gray = 0.2126 * r + 0.7152 * g + 0.0722 * b
這樣就好辦了,我們可以創建一個不重復的字符列表,灰度值小(暗)的用列表開頭的符號,灰度值大(亮)的用列表末尾的符號。
三、實驗步驟
PIL 是一個 Python 圖像處理庫,是本課程使用的重要工具,安裝 pillow(PIL)庫:
$ sudo apt-get update
$ sudo apt-get install python-dev
$ sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev \
libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
$ sudo pip install pillow
首先獲取實驗用圖片
$ wget http://labfile.oss.aliyuncs.com/courses/370/ascii_dora.png
創建 ascii.py 文件進行編輯
$ vi ascii.py
首先導入必要的庫,argparse 庫是用來管理命令行參數輸入的
from PIL import Image import argparse
下面是我們的字符畫所使用的字符集,一共有 70 個字符,字符的種類與數量可以自己根據字符畫的效果反復調試
ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
下面是RGB值轉字符的函數:
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) unit = (256.0 + 1)/length return ascii_char[int(gray/unit)]
完整參考代碼:
from PIL import Image import argparse #命令行輸入參數處理 parser = argparse.ArgumentParser() parser.add_argument('file') #輸入文件 parser.add_argument('-o', '--output') #輸出文件 parser.add_argument('--width', type = int, default = 80) #輸出字符畫寬 parser.add_argument('--height', type = int, default = 80) #輸出字符畫高 #獲取參數 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) unit = (256.0 + 1)/length return ascii_char[int(gray/unit)] 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)
輸入以下命令運行腳本查看實驗效果
$ python ascii.py ascii_dora.png
注意,不同的環境中顯示的效果可能不盡相同
終端顯示的字體是不是等寬字體,終端顯示的行高和行寬,輸入輸出的圖像寬高等等,這些都會影響顯示效果
getpixel : 返回指定位置的像素,如果所打開的圖像是多層次的圖片,那這個方法就返回一個元組。
im.getpixel( xy ) => value or tuple
本實驗中的函數,我的理解是,圖片模式應該是RGBA:4元素元組, 而且:def get_char(r,g,b,alpha = 256),這個函數里邊也是4個參數。
然后,說下我對這個程序的理解: txt += get_char(im.getpixel((j,i))) 這個函數,首先調用了im.getpixel函數,im.getpixel的參數是(j,i)。(j,i)其實是圖片的橫縱坐標。通過調用這個函數,把圖片的橫縱坐標上的顏色,分割成了(r,g,b,alpha)這個四個參數,然后調用get_char這個函數。 再說下get_char這個函數是怎么運行的。 def get_char(r,g,b,alpha = 256): if alpha == 0: //如果alpha等於0,也就是判斷圖片現在是不是沒有了。 return ' ' length = len(ascii_char)//就是上邊那一串字符串的長度 gray = int(0.2126 r + 0.7152 g + 0.0722 b) //灰度的計算公式
unit = (256.0 + 1)/length return ascii_char[int(gray/unit)] //其實這個就是gray*length/257,這個公式相當於按照灰度,在那串字符串中選一個字母。。。
為了下次看到能夠有更深的印象,我對程序做一下注釋:
from PIL import Image #從PIL模塊中引入Image這個類 import argparse #引入argparse這個模塊。argparse 庫是用來管理命令行參數輸入的
############################################################
1:import argparse
2:parser = argparse.ArgumentParser()
3:parser.add_argument()
4:parser.parse_args()
解釋:首先導入該模塊;然后創建一個解析對象;
然后向該對象中添加你要關注的命令行參數和選項
,每一個add_argument方法對應一個你要關注的參數或選項;
最后調用parse_args()方法進行解析;解析成功之后即可使用
###################################
#命令行輸入參數處理 parser = argparse.ArgumentParser() parser.add_argument('file') #輸入文件 parser.add_argument('-o', '--output') #輸出文件 parser.add_argument('--width', type = int, default = 80) #輸出字符畫寬 parser.add_argument('--height', type = int, default = 80) #輸出字符畫高 #獲取參數 args = parser.parse_args() IMG = args.file WIDTH = args.width HEIGHT = args.height OUTPUT = args.output
#定義一個ascii的列表,其實就是讓圖片上的灰度與字符對應 ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ") # 將256灰度映射到70個字符上 def get_char(r,g,b,alpha = 256): #這個調用跟
im.getpixel函數有關,這個函數是根據圖片的橫縱坐標,把圖片解析成r,g,b,alpha(灰度),有關的四個參數,所以這里輸入參數是四個
if alpha == 0: #如果灰度是0,說明這里沒有圖片 return ' ' length = len(ascii_char) #計算這些字符的長度 gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b) #把圖片的RGB值轉換成灰度值 unit = (256.0 + 1)/length #257/length return ascii_char[int(gray/unit)] #這個相當於是選出了灰度與哪個字符對應。 if __name__ == '__main__': #如果是本程序調用,則執行以下程序 im = Image.open(IMG) #打開圖片 im = im.resize((WIDTH,HEIGHT), Image.NEAREST) #更改圖片的顯示比例 txt = "" #txt初始值為空 for i in range(HEIGHT): #i代表縱坐標 for j in range(WIDTH): #j代表橫坐標 txt += get_char(*im.getpixel((j,i))) #把圖片按照橫縱坐標解析成r,g,b以及alpha這幾個參數,然后調用get_char函數,把對應的圖片轉換成灰度值,把對應值得字符存入txt中 txt += '\n' #每行的結尾處,自動換行 print txt #在界面打印txt文件 #字符畫輸出到文件 if OUTPUT: with open(OUTPUT,'w') as f: #文件輸出 f.write(txt) else: with open("output.txt",'w') as f: #文件輸出 f.write(txt)