Python 實(shí)現(xiàn)圖片轉(zhuǎn)字符畫,靜態(tài)圖、動(dòng)態(tài)圖都能轉(zhuǎn)!

文 |?野客
來源:Python 技術(shù)「ID: pythonall」

字符畫是一種由字母、標(biāo)點(diǎn)或其他字符組成的圖畫,它產(chǎn)生于互聯(lián)網(wǎng)時(shí)代,在聊天軟件中使用較多,本文我們看一下如何將自己喜歡的圖片轉(zhuǎn)成字符畫。
靜態(tài)圖片
首先,我們來演示將靜態(tài)圖片轉(zhuǎn)為字符畫,功能實(shí)現(xiàn)主要用到的 Python 庫為 OpenCV,安裝使用?pip install opencv-python?命令即可。
功能實(shí)現(xiàn)的基本思路為:利用聚類將像素信息聚為 3 或 5 類,顏色最深的一類用數(shù)字密集度表示,陰影的一類用橫杠(-)表示,明亮部分用空白表示。
主要代碼實(shí)現(xiàn)如下:
def?img2strimg(frame,?K=5):???
????if?type(frame)?!=?np.ndarray:
????????frame?=?np.array(frame)
????height,?width,?*_?=?frame.shape??
????frame_gray?=?cv2.cvtColor(frame,?cv2.COLOR_BGR2GRAY)
????frame_array?=?np.float32(frame_gray.reshape(-1))
????criteria?=?(cv2.TERM_CRITERIA_EPS?+?cv2.TERM_CRITERIA_MAX_ITER,?10,?1.0)
????flags?=?cv2.KMEANS_RANDOM_CENTERS
????#?得到?labels(類別)、centroids(矩心)
????compactness,?labels,?centroids?=?cv2.kmeans(frame_array,?K,?None,?criteria,?10,?flags)
????centroids?=?np.uint8(centroids)
????#?labels?的數(shù)個(gè)矩心以隨機(jī)順序排列,所以需要簡單處理矩心
????centroids?=?centroids.flatten()
????centroids_sorted?=?sorted(centroids)
????#?獲得不同?centroids?的明暗程度,0?為最暗
????centroids_index?=?np.array([centroids_sorted.index(value)?for?value?in?centroids])
????bright?=?[abs((3?*?i?-?2?*?K)?/?(3?*?K))?for?i?in?range(1,?1?+?K)]
????bright_bound?=?bright.index(np.min(bright))
????shadow?=?[abs((3?*?i?-?K)?/?(3?*?K))?for?i?in?range(1,?1?+?K)]
????shadow_bound?=?shadow.index(np.min(shadow))
????labels?=?labels.flatten()
????#?將?labels?轉(zhuǎn)變?yōu)閷?shí)際的明暗程度列表
????labels?=?centroids_index[labels]
????#?解析列表
????labels_picked?=?[labels[rows?*?width:(rows?+?1)?*?width:2]?for?rows?in?range(0,?height,?2)]
????canvas?=?np.zeros((3?*?height,?3?*?width,?3),?np.uint8)
?#?創(chuàng)建長寬為原圖三倍的白色畫布
????canvas.fill(255)
????y?=?8
????for?rows?in?labels_picked:
????????x?=?0
????????for?cols?in?rows:
????????????if?cols?<=?shadow_bound:
????????????????cv2.putText(canvas,?str(random.randint(2,?9)),
????????????????????????????(x,?y),?cv2.FONT_HERSHEY_PLAIN,?0.45,?1)
????????????elif?cols?<=?bright_bound:
????????????????cv2.putText(canvas,?"-",?(x,?y),
????????????????????????????cv2.FONT_HERSHEY_PLAIN,?0.4,?0,?1)
????????????x?+=?6
????????y?+=?6
????return?canvas
原圖如下:

效果圖如下:

GIF 動(dòng)圖
接下來我們演示將 GIF 轉(zhuǎn)為字符畫,功能實(shí)現(xiàn)主要用到的 Python 庫為 imageio、Pillow,安裝使用?pip install imageio/Pillow?命令即可。
功能實(shí)現(xiàn)的基本思路如下:
將 gif 圖片的每一幀拆分為靜態(tài)圖片 將所有靜態(tài)圖片變?yōu)樽址?/section> 將所有字符畫重新合成 gif
主要代碼實(shí)現(xiàn)如下:
#?拆分 gif 將每一幀處理成字符畫
def?gif2pic(file,?ascii_chars,?isgray,?font,?scale):
????'''
????file:?gif?文件
????ascii_chars:?灰度值對(duì)應(yīng)的字符串
????isgray:?是否黑白
????font:?ImageFont?對(duì)象
????scale:?縮放比例
????'''
????im?=?Image.open(file)
????path?=?os.getcwd()
????if(not?os.path.exists(path+"/tmp")):
????????os.mkdir(path+"/tmp")
????os.chdir(path+"/tmp")
????#?清空?tmp?目錄下內(nèi)容
????for?f?in?os.listdir(path+"/tmp"):
????????os.remove(f)
????try:
????????while?1:
????????????current?=?im.tell()
????????????name?=?file.split('.')[0]+'_tmp_'+str(current)+'.png'
????????????#?保存每一幀圖片
????????????im.save(name)
????????????#?將每一幀處理為字符畫
????????????img2ascii(name,?ascii_chars,?isgray,?font,?scale)
????????????#?繼續(xù)處理下一幀
????????????im.seek(current+1)
????except:
????????os.chdir(path)
#?將不同的灰度值映射為?ASCII?字符
def?get_char(ascii_chars,?r,?g,?b):
????length?=?len(ascii_chars)
????gray?=?int(0.2126?*?r?+?0.7152?*?g?+?0.0722?*?b)
????return?ascii_chars[int(gray/(256/length))]
#?將圖片處理成字符畫
def?img2ascii(img,?ascii_chars,?isgray,?font,?scale):
????scale?=?scale
????#?將圖片轉(zhuǎn)換為?RGB?模式
????im?=?Image.open(img).convert('RGB')
????#?設(shè)定處理后的字符畫大小
????raw_width?=?int(im.width?*?scale)
????raw_height?=?int(im.height?*?scale)
????#?獲取設(shè)定的字體的尺寸
????font_x,?font_y?=?font.getsize('?')
????#?確定單元的大小
????block_x?=?int(font_x?*?scale)
????block_y?=?int(font_y?*?scale)
????#?確定長寬各有幾個(gè)單元
????w?=?int(raw_width/block_x)
????h?=?int(raw_height/block_y)
????#?將每個(gè)單元縮小為一個(gè)像素
????im?=?im.resize((w,?h),?Image.NEAREST)
????#?txts?和?colors?分別存儲(chǔ)對(duì)應(yīng)塊的?ASCII?字符和?RGB?值
????txts?=?[]
????colors?=?[]
????for?i?in?range(h):
????????line?=?''
????????lineColor?=?[]
????????for?j?in?range(w):
????????????pixel?=?im.getpixel((j,?i))
????????????lineColor.append((pixel[0],?pixel[1],?pixel[2]))
????????????line?+=?get_char(ascii_chars,?pixel[0],?pixel[1],?pixel[2])
????????txts.append(line)
????????colors.append(lineColor)
????#?創(chuàng)建新畫布
????img_txt?=?Image.new('RGB',?(raw_width,?raw_height),?(255,?255,?255))
????#?創(chuàng)建?ImageDraw?對(duì)象以寫入?ASCII
????draw?=?ImageDraw.Draw(img_txt)
????for?j?in?range(len(txts)):
????????for?i?in?range(len(txts[0])):
????????????if?isgray:
????????????????draw.text((i?*?block_x,?j?*?block_y),?txts[j][i],?(119,136,153))
????????????else:
????????????????draw.text((i?*?block_x,?j?*?block_y),?txts[j][i],?colors[j][i])
????img_txt.save(img)
#?讀取?tmp?目錄下文件合成?gif
def?pic2gif(dir_name,?out_name,?duration):
????path?=?os.getcwd()
????os.chdir(dir_name)
????dirs?=?os.listdir()
????images?=?[]
????num?=?0
????for?d?in?dirs:
????????images.append(imageio.imread(d))
????????num?+=?1
????os.chdir(path)
????imageio.mimsave(out_name?+?'_ascii.gif',images,duration?=?duration)
原圖如下:

黑白效果圖如下:

彩色效果圖如下:

總結(jié)
本文我們利用 Python 演示了將靜態(tài)圖和 GIF 轉(zhuǎn)為字符畫的方法,大家如果有興趣的話,可以將自己喜歡的圖轉(zhuǎn)一下,如果對(duì)轉(zhuǎn)換效果不滿意,還可以修改代碼,改成自己滿意的效果。
-END-
評(píng)論
圖片
表情
