基于Python实现图画转换字符画

微信改版,加星标不迷路!

640?wx_fmt=gif

基于Python实现图画转换字符画

作者:阿广

概述

  • 前言

  • 准备工作

  • 识别原理

  • 静态图像处理

  • 动态图像处理

  • 结果

  • 阿广说

  • 群聊交流

  • 福利一刻

  • 推荐阅读

前言

前几天写了基于Python生成铅笔素描图,朋友们对其非常感兴趣。不满足于现状,却局限于现状,对静态和动态的图画进行了字符画的转变,何尝不呈现出“几多风流、几多闲愁”的诗情画意。我不是画画的创造者,我是图画的修理工。

准备工作

对于静态图片,我们使用PIL做图像处理,所以需要安装下面的库:

 
 

from PIL import Image, ImageDraw, ImageFont
import os
import time

对于动态图片,我们需要导入以下库:

 
 

from PIL import Image, ImageFont, ImageDraw
import argparse
import os
import imageio

识别原理

字符画是一系列字符的组合,我们可以把字符看作是比较大块的像素,一个字符能表现一种颜色,字符的种类越多,可以表现的颜色也越多,图片也会更有层次感。

问题来了,我们是要转换一张彩色的图片,这么多的颜色,要怎么对应到单色的字符画上去?这里就要介绍灰度值的概念了。

灰度值:指黑白图像中点的颜色深度,范围一般从0到255,白色为255,黑色为0,故黑白图片也称灰度图像

我们可以使用灰度值公式将像素的 RGB 值映射到灰度值:

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

这样就好办了,我们可以创建一个不重复的字符列表,灰度值小的用列表开头的符号,灰度值大的用列表末尾的符号。

静态图像处理

主函数如下:

def main():
   im = Image.open(img_name + '.jpg').convert('L')
   im = im.resize((f_num_x, int(f_num_x * im.size[1] / im.size[0])))
   level = im.getextrema()[-1] / (len(font_map) - 1)
   im = im.point(lambda i: int(i / level))
   imn = Image.new('L', (im.size[0] * f_size, im.size[1] * f_size))

   f = ImageFont.truetype('arial.ttf', f_size)
   d = ImageDraw.Draw(imn)

   for y in range(0, im.size[1]):
       for x in range(0, im.size[0]):
           pp = im.getpixel((x, y))
           d.text((x * f_size, y * f_size), font_map[len(font_map) - pp - 1], fill=255, font=f)

   save(imn, img_name)

 

将处理之后的图片保存下来:

def save(img, file_name):
   if os.path.isfile(file_name + '.jpg'):
       save(img, file_name + '-')
   else:
       img.save(file_name + '.jpg', 'JPEG')


调用主函数:

if __name__ == '__main__':
   tt = time.time()
   main()
   print(time.time() - tt)

动态图像处理

命令行输入参数处理:

parser = argparse.ArgumentParser()
parser.add_argument('file')
parser.add_argument('-d','--duration',type = float, default = 1)#播放时间


获取参数:

args = parser.parse_args()
File = args.file
DURARION = args.duration


像素对应ASCII码:

ascii_char = list("MNHQ$OC67+>!:-. ")


获取ASCCI码:

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)]


将txt转化为图片:

def txt2png(file_name):
   im = Image.open(file_name).convert('RGB')
   #gif拆分后的图像,需要转换,否则报错,由于gif分割后保存的是索引颜色
   raw_width = im.width  
   raw_height = im.height
   width = int(raw_width/6)
   height = int(raw_height/15)
   im = im.resize((width,height),Image.NEAREST)
   txt=""
   colors = []
   for i in range(height):
       for j in range(width):
           pixel = im.getpixel((j,i))
           colors.append((pixel[0],pixel[1],pixel[2]))
           if(len(pixel) == 4):
               txt += get_char(pixel[0],pixel[1],pixel[2],pixel[3])
           else:
               txt += get_char(pixel[0],pixel[1],pixel[2])        
       txt += '\n'
       colors.append((255,255,255))
   im_txt = Image.new("RGB",(raw_width,raw_height),(255,255,255))
   dr = ImageDraw.Draw(im_txt)
   font=ImageFont.load_default().font
   x=y=0
   font_w,font_h=font.getsize(txt[1])    #获取字体的宽高
   font_h *= 1.37 #调整后更佳
   #ImageDraw为每个ascii码进行上色
   for i in range(len(txt)):
       if(txt[i]=='\n'):
           x+=font_h
           y=-font_w
       dr.text([y,x],txt[i],colors[i])
       y+=font_w
   name = file_name.split('.')[0]+'-txt'+'.png'
   print(name)
   im_txt.save(name)


拆分gif:

def gif2png(file_name):
   im = Image.open(file_name)
   path = os.getcwd()
   if(not os.path.exists(path+"/Cache")):
       os.mkdir(path+"/Cache")
   os.chdir(path+"/Cache")
   try:
       while 1:
           current = im.tell()
           name = file_name.split('.')[0]+'-'+str(current)+'.png'
           im.save(name)#gif分割后保存的是索引颜色
           print(name)
           txt2png(name)
           im.seek(current+1)
   except:
       os.chdir(path)


转换为gif:

def png2gif(dir_name):
   path = os.getcwd()
   os.chdir(dir_name)
   dirs = os.listdir()
   images = []
   num = 0
   for d in dirs:
       if d.split('-')[-1] == 'txt.png':
           images.append(imageio.imread(d))
           num += 1
   os.chdir(path)
   imageio.mimsave(d.split('-')[0]+'-txt_c.gif',images,duration = DURARION)


主函数:

if __name__=='__main__':
   gif2png(File)
   path = os.getcwd()
   png2gif(path+"/Cache")

详情请查看源码请自行去github,或者点击文末的阅读原文获取源码。

https://github.com/zandaoguang/pic_to_char

结果

640?wx_fmt=jpeg


当我跑完代码之后,出现了上边这张黑糊糊的图,这是什么鬼?

640

看了下边这个图才看出来一些模样~

确实好丑啊


640?wx_fmt=jpeg


从清华巧那儿学到了爱的五种语言


1.语言上对对方的赞美


2.物质上的表达


3.拿出一些时间陪伴对方


4.讨好彼此,服务彼此的行动;你喜欢的,我为你泡杯茶


5.身体上的接触



640?wx_fmt=jpeg


敲得代码都是爱你的形状。


640?wx_fmt=jpeg


爱情是什么?我把它比作


张爱玲的

“你还不来,我怎敢老去?”


方文山的

“天青色等烟雨,而我在等你。”


元稹的

“曾经沧海难为水,除却巫山不是云”


冯唐的

“春水初生,春林初盛,春风十里,不如你。”


640?wx_fmt=jpeg


红豆生南国,春来发几枝。 


愿君多采撷,此物最相思。


640?wx_fmt=jpeg



640?wx_fmt=gif


醒醒,别一直看这个图了!


(我至少看了n遍)


640?wx_fmt=gif

活着本来没有什么意义


但是活着,就可以找到有趣的事情。


就像你发现了这多花


而我发现了你


640?wx_fmt=gif


人生在世只有一次


不必勉强选择自己不喜欢的路


随性而生或随性而死都没关系


不过,无论选择哪条路


都不要忘记保护自己所珍惜的人


640?wx_fmt=gif

群聊交流    

    640?

    我创建了两个群,为读者提供了分享知识、传递正能量的平台:


640?

视学算法硕博学术交流群

视学算法就业交流群


640?

添加学姐微信,拉你进群

务必备注:硕博或者就业


640?wx_fmt=png


福利一刻

视学算法特此感谢电子工业出版社的独家赞助,电子工业出版社已出版《你也能看得懂的Python算法书》

此前,本书正处于预售状态。11月26号视学算法将和电子工业出版社联合赠送数十本。

推荐阅读

640

程序员脱单示范2:女神和兄弟哪个更重要?

640?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值