| 作者:阿广(中科院在读博士生)
| 来源:视学算法
在完成基于Python生成铅笔素描图后,朋友们对其非常感兴趣。不满足于现状,却局限于现状,对静态和动态的图画进行了字符画的转变,何尝不呈现出“几多风流、几多闲愁”的诗情画意。我不是画画的创造者,我是图画的修理工。
准备工作
对于静态图片,我们使用PIL做图像处理,所以需要安装下面的库:
fromPIL importImage, ImageDraw, ImageFont
importos
importtime
对于动态图片,我们需要导入以下库:
fromPIL importImage, ImageFont, ImageDraw
importargparse
importos
importimageio
识别原理
字符画是一系列字符的组合,我们可以把字符看作是比较大块的像素,一个字符能表现一种颜色,字符的种类越多,可以表现的颜色也越多,图片也会更有层次感。
问题来了,我们是要转换一张彩色的图片,这么多的颜色,要怎么对应到单色的字符画上去?这里就要介绍灰度值的概念了。
灰度值:指黑白图像中点的颜色深度,范围一般从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)
foryin range(0, im.size[1]):
forxin 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)
将处理之后的图片保存下来:
defsave(img, file_name):
ifos.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):
ifalpha == 0:
return''
length = len(ascii_char)
gray = int(0.2126* r + 0.7152* g + 0.0722* b)
unit = (256.0+1)/length
returnascii_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 = []
fori in range(height):
forjin 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码进行上色
fori 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:
while1:
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
ford in dirs:
ifd.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
结果
当我跑完代码之后,出现了上边这张黑糊糊的图,这是什么鬼?
看了下边这个图才看出来一些模样~
确实好丑啊
从清华巧那儿学到了爱的五种语言
1.语言上对对方的赞美
2.物质上的表达
3.拿出一些时间陪伴对方
4.讨好彼此,服务彼此的行动;你喜欢的,我为你泡杯茶
5.身体上的接触
敲得代码都是爱你的形状。
爱情是什么?我把它比作
张爱玲的
“你还不来,我怎敢老去?”
方文山的
“天青色等烟雨,而我在等你。”
元稹的
“曾经沧海难为水,除却巫山不是云”
冯唐的
“春水初生,春林初盛,春风十里,不如你。”
红豆生南国,春来发几枝。
愿君多采撷,此物最相思。
醒醒,别一直看这个图了!
(我至少看了n遍)
活着本来没有什么意义
但是活着,就可以找到有趣的事情。
就像你发现了这多花
而我发现了你
人生在世只有一次
不必勉强选择自己不喜欢的路
随性而生或随性而死都没关系
不过,无论选择哪条路
都不要忘记保护自己所珍惜的人