Python極客項目編程之ASCII文本圖形
ASCII文本圖形
前言
??ASCII文本圖形源頭是19世紀后期出現的打字機文本圖形。在20世紀60年代,計算機有了較弱的圖形處理硬件,ASCII被用于表示圖形。今天,ASCII文本圖形繼續作為因特網上的一種表現形式,你可以在網上找到各種創意的例子。
一、學習目標及步驟
??這個項目用Python創建一個程序,從圖像生成ASCII文本圖形。該程序讓你指定輸出文本序列的寬度,并設置垂直比例因子。它也支持兩種灰度值到ASCII字符的映射:稀疏的10級映射和更精細校正的70級映射。
??要從圖像生成ASCII文本圖像,需要學習如何做到以下幾點:
- 用Pillow將彩色圖像轉換成灰度圖像,它是Python的圖像庫(PIL)的一個分支;
- 使用numpy計算灰度圖像的平均亮度;
- 用一個字符串作為灰度值的快速查找表。
下面是程序生成ASCII文本圖形的步驟:
1.?將輸入的圖形裝成灰度;
2.?將圖像分成M×N個小塊;
3.?修正M(行數),以匹配圖像和字體的橫縱比;
4.?計算每一個小塊圖像的平均亮度,然后為每個小塊查找合適的ASCII字符;
5.?匯集各行的ASCII字符串,將他們打印到文件,形成最終圖像。
二、代碼
1.引入庫
代碼如下(示例):
import argparse
import numpy as np
from PIL import Image
2.定義灰度等級和網格
??先定義兩種灰度等級作為全局值,分別是70級,10級,這兩個值保存為字符串,從最黑暗變到最亮,用于將亮度值轉換為ASCII字符。
# 70 levels of gray
gscale1 = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^.y' "
# 10 leves of gray
gscale2 = "@%#*+=-:. "
??既然有了灰度梯度,就可以準備圖像了。下面的代碼打開圖像,并分割成網格:
# 打開圖像并轉換為灰度
image = Image.open(filename).convert('L')
# 存儲圖像尺寸
W, H = image.size[0], image.size[1]
# 計算平鋪寬度
w = W/cols
# 根據字體的縱橫比和比例計算平鋪高度
h = w/scale
# 計算要在最終網格中使用的行數
rows = int(H/h)
??比例系數確定每個小塊的大小,以匹配用于顯示文字的字體的橫縱比,這樣最終圖像不會失真。scale的值可以作為參數傳入,或者設置位默認值0.43,在用Courier顯示結果時,效果最好。
3.計算平均亮度
??計算灰度圖像中每一小塊的平均亮度。函數getAverageL()完成。
def getAverageL(image):
# 以numpy數組的形式獲取圖像
im = np.array(image)
# 得到尺寸
w, h = im.shape
# 獲得平均值
return np.average(im.reshape(w*h))
??圖像小塊作為PIL Image對象傳入,將 image轉換成一個numpy數組,此時 im成為一個二維數組,包含每個像素的亮度。保存該圖像的尺寸(寬度和高度)。numpy.average()計算該圖像中的亮度平均值,做法是用numpy.reshape()先將維度為寬和高(w,h)的二維數組轉換成扁平的一維,其長度是寬度乘以高度(w*h)。然后numpy.average()調用對這些數組值求和并計算平均值。
4.從圖像生成ASCII內容
# ASCII圖像是字符串的列表
aimg = []
# 生成平鋪尺寸列表
for j in range(rows):
y1 = int(j * h)
y2 = int((j + 1) * h)
# 更正最后一個平鋪方塊
if j == rows - 1:
y2 = H
# 追加一個空字符串
aimg.append("")
for i in range(cols):
# 裁剪圖像以適合平鋪
x1 = int(i * w)
x2 = int((i + 1) * w)
# 更正最后一個平鋪方塊
if i == cols - 1:
x2 = W
# 裁剪圖像以將其平鋪提取到另一個圖像對象中
img = image.crop((x1, y1, x2, y2))
# 獲得平均亮度
avg = int(getAverageL(img))
# 在ascii字符中查找平均灰度值
if moreLevels:
gsval = gscale1[int((avg * 69) / 255)]
else:
gsval = gscale2[int((avg * 9) / 255)]
# 將ascii字符附加到字符串
aimg[j] += gsval
??在程序的這一部分,ASCII圖像先作為一個字符串列表保存并初始化。接下來,按計算好的圖像小塊行數迭代遍歷,計算每個圖像小塊的起始和結束y坐標。雖然這些是浮點運算,但在傳給圖像裁剪方法之前,將它們截斷為整數。接著,因為只有當圖像的寬度是列數的整數倍時,圖像分割成小塊時,邊緣的小塊才有相同的大小,所以在最后一行校正小塊的y坐標,將y坐標設置為圖像的實際高度。這樣做確保了圖像頂部的邊緣不被截斷。然后為ASCII 圖像添加一個空字符串,作為一種緊湊的方式來表示圖像的當前行。接下來會填充這個字符串(將字符串作為字符的列表)。
??計算每個小塊的左、右x坐標,為最后一小塊校正x坐標,原因和校正y坐標時一樣。然后用 image.crop()提取圖像小塊,然后將該小塊傳入getAverageL()函數,取得小塊的平均亮度。將平均亮度值從[0,255]縮小至[0,9](默認10級灰度梯度值的范圍)。然后,用 gscale2(保存的梯度字符串)作為查找表,找到對應的ASCII 值。然后類似,不同之處在于,只有命令行標志設置為使用70級梯度時,才會用它。最后,在文本行中添加找到的ASCII 值 gsval,代碼循環,直到處理完所有行。
5.類初始化定義
descStr = "This program converts an image into ASCII art."
# 添加預期參數
parser = argparse.ArgumentParser(description=descStr)
parser.add_argument('--file', dest='imgFile', required=False)
parser.add_argument('--scale', dest='scale', required=False)
parser.add_argument('--out', dest='outFile', required=False)
parser.add_argument('--cols', dest='cols', required=False)
parser.add_argument('--morelevels', dest='moreLevels', action='store_true')
??指定圖像文件輸入的選項(唯一必須的參數),設置垂直比例因子,設置輸出文件名,設置ASCII輸出中的文本列數。在行,添加–morelevels選項,讓用戶選擇更多層次的灰度梯度。
6.將ASCII文本圖形字符串寫入文本文件
# 打開一個新文件
f = open(outFile, 'w')
# 將列表中的每個字符串寫入新文件
for row in aimg:
f.write(row + '\n')
# 關閉文件
f.close()
??使用內置的 open()方法,打開一個新的文本文件用于寫入。然后迭代遍歷列表中的每個字符串,將它寫入文件,關閉文件對象,釋放系統資源。
三、完整代碼
import argparse
from PIL import Image
import numpy as np
# 70 leves of gray
gscale1 = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^.y' "
# 10 leves of gray
gscale2 = "@%#*+=-:. "
def getAverageL(image):
# 以numpy數組的形式獲取圖像
im = np.array(image)
# 得到尺寸
w, h = im.shape
# 獲得平均值
return np.average(im.reshape(w*h))
def covertImageToAscii(filename, cols, scale, moreLevels):
global gscale1, gscale2
# 打開圖像并轉換為灰度
image = Image.open(filename).convert('L')
# 存儲圖像尺寸
W, H = image.size[0], image.size[1]
# print("input image dims: %d * %d" % (W, H))
# 計算平鋪寬度
w = W/cols
# 根據字體的縱橫比和比例計算平鋪高度
h = w/scale
# 計算要在最終網格中使用的行數
rows = int(H/h)
# print("col: %d, row: %d" % (cols, rows))
# print("tile dims: %d * %d" % (w, h))
if cols > W or rows > H:
# print("Image too small for specified cols!")
exit(0)
# ASCII圖像是字符串的列表
aimg = []
# 生成平鋪尺寸列表
for j in range(rows):
y1 = int(j * h)
y2 = int((j + 1) * h)
# 更正最后一個平鋪方塊
if j == rows - 1:
y2 = H
# 追加一個空字符串
aimg.append("")
for i in range(cols):
# 裁剪圖像以適合平鋪
x1 = int(i * w)
x2 = int((i + 1) * w)
# 更正最后一個平鋪方塊
if i == cols - 1:
x2 = W
# 裁剪圖像以將其平鋪提取到另一個圖像對象中
img = image.crop((x1, y1, x2, y2))
# 獲得平均亮度
avg = int(getAverageL(img))
# 在ascii字符中查找平均灰度值
if moreLevels:
gsval = gscale1[int((avg * 69) / 255)]
else:
gsval = gscale2[int((avg * 9) / 255)]
# 將ascii字符附加到字符串
aimg[j] += gsval
return aimg
def main(imgFile, scale=0.52, cols=65, outFile='out.txt'):
descStr = "This program converts an image into ASCII art."
# 添加預期參數
parser = argparse.ArgumentParser(description=descStr)
parser.add_argument('--file', dest='imgFile', required=False)
parser.add_argument('--scale', dest='scale', required=False)
parser.add_argument('--out', dest='outFile', required=False)
parser.add_argument('--cols', dest='cols', required=False)
parser.add_argument('--morelevels', dest='moreLevels', action='store_true')
args = parser.parse_args()
if args.outFile:
outFile = args.outFile
if args.scale:
scale = float(args.scale)
if args.cols:
cols = int(args.cols)
# print('generating ASCII art ...')
aimg = covertImageToAscii(imgFile, cols, scale, args.moreLevels)
# 打開一個新文件
f = open(outFile, 'w')
# 將列表中的每個字符串寫入新文件
for row in aimg:
print(row)
f.write(row + '\n')
# 關閉文件
f.close()
# print("ASCII art written to %s" % outFile)
if __name__ == '__main__':
main('fxp.jpg')
四、運行結果
??在這個項目中,我們學習了如何從任意的輸入圖像生成ASCII文本圖形,還學習了計算平均亮度值,將圖片轉換成灰度,以及如何基于灰度值用字符替換一小塊圖像,對比原圖和運行結果圖,代碼基本運行成功。(其實代碼也是閑著沒事給某個小姑娘寫的,順便學習一下python語法)
參考代碼:https://github.com/electronut/pp/blob/master/ascii/ascii.py
智能推薦
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...
猜你喜歡
統計學習方法 - 樸素貝葉斯
引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...
styled-components —— React 中的 CSS 最佳實踐
https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...
19.vue中封裝echarts組件
19.vue中封裝echarts組件 1.效果圖 2.echarts組件 3.使用組件 按照組件格式整理好數據格式 傳入組件 home.vue 4.接口返回數據格式...