一、所需库

PIL:python的一个图形处理库

os:python的一个标准库,提供操作系统功能的一些函数

二、思路

我们都知道图片都是由一个一个的像素点组成的(如下图所示),每个像素点又由三种颜色R红色、G绿色、B蓝色各自按0到255组合而成,颜色所代表的数字越大,这个颜色在这个像素点中越亮,反之越暗,比如白色的RGB值为(255,255,255),黑色的RGB值为(0,0,0)。RGB一共有256×256×256=16777216种组合!

如果我们将图片变为灰度图

看起来是不是像黑白图片,但是在灰度图中其实不止有纯黑纯白这两种颜色,灰度图的像素在黑色和白色中间还有许多过渡的颜色,分为256阶,0到255,数字越大越亮,越小越暗,因为灰度图的像素只用一个整数表示,所以一个像素就只有256种可能,那样对于图片转为字符画来说那将变得非常容易。

我们可以列出一串字符串表示灰度值从暗到亮的像素(最后一个字符为空格)

$#%@&MNBEFRWYLIkbtj?*984532menocvzst{}[]1|()<>=+~-;:i^"'.

这个没有固定数量与排序,不一定要非常多个,不过如果排列得当且数量多一些,最后呈现出来的字符画会更加精细一些。

看到这里原理已经非常地明显了,将每个像素点按灰度值的大小选择相对黑白像素密集程度的字符,并按原图片的像素顺序排序,这样字符画就可以出现啦!那么,我们开始动手吧!

三、操作

首先第一步,我们要知道像素的灰度值,我们平时用到的图片基本上都是用RGB表示像素的,不知道灰度值怎么办呢?所以这里我们要引入一个公式:

Gray=R*0.299 + G*0.587 + B*0.114

这个公式可以让我们根据像素的RGB的值算出对应的灰度值,

比如纯黑色(0,0,0)的灰度值为0*0.299+0*587+0*0.114=0 ,

纯白色(255,255,255)的灰度值为255*0.299+255*0.587+255*0.114=255

灰度值的问题解决了,接下来我们来解决一下如何将灰度值转换为对应复杂程度的字符

def g2s(gray):#设置参数输入像素的灰度值
    pixel_str='''$#%@&MNBEFRWYLIkbtj?*984532menocvzst{}[]1|()<>=+~-;:i^"'. '''
    length=len(pixel_str)
    plus=255/length #字符之间的灰度区间
    str_gray=0 #str_gray表示字符所代表的灰度值
    for i in range(length):
        str_gray = str_gray + plus
        if gray <=str_gray:
            return pixel_str[i]

我们来测试一下

>>>g2s(255)
' '
>>>g2s(0)
'$'
>>>g2s(250)
'.'

可以了!不过这里需要注意一点的是plus的结果是浮点数,根据pixel_str的长度计算出浮点数,若你写的长度跟我的不一样就可能会出现plus全部加完了还是比255小的情况,从而导致函数无法输出结果,这时可以将str_gray的初始值设置为1就可以正常运行了。

下一步我们开始写主函数

from PIL import Image
import os

def img2str(img_path,save_path,num=0):
    txt_path=os.path.join(save_path,'img.txt')
    f=open(txt_path,'w')
    f.write('')
    f.close() #因为此目录有可能已有内容,所以先清空
    f=open(txt_path,'a') #a表示在文件的末尾添加

    im=Image.open(img_path)
    if num==0:#因为有些图片尺寸过于大,所以添加了一个修改大小的功能
        pass
    else:
        im=im.resize( ( int(im.size[0]/num),int(im.size[1]/num) ) )

    im=im.convert('RGB')#因为有些图片不是RGB格式的,所以我们先要将其转换成RGB格式
    for y in range(im.size[1]):
        for x in range(im.size[0]):
            r,g,b=im.getpixel((x,y))[0],im.getpixel((x,y))[1],im.getpixel((x,y))[2]
            gray=r*0.299+g*0.587+b*0.114 #代入公式
            s=g2s(gray) #我们在上面实现的函数
            f.write(s)
        f.write('\n') #每一行结束后换行
    f.close()

当然,还有更简单的写法

    im=im.convert('L')#直接将图片转换成灰度模式
    for y in range(im.size[1]):
        for x in range(im.size[0]):
            s=g2s(im.getpixel((x,y)))
            f.write(s)
        f.write('\n')
    f.close()

不过为了运用上面所学到知识,这里就用上面那一版。

四、测试

准备一张图片test.jpg

在文档末尾添加以下代码,根据你的路径修改参数

if __name__=='__main__':
    img2str(r'C:\\Users\\Felix\\Desktop\\test.jpg',r'C:\Users\Felix\Desktop',4)

运行程序,在桌面生成文件img.txt,打开查看,成功!如果你的字符画看起来很混乱的话,请将自动换行关闭。

总结

图片转字符画的讲解就到这里了,代表像素的字符串如果设置得合理的话,那么生成的字符画会更加地细致一些。你可以为这个程序打造一个gui界面,我之前就用过tkinter为这个程序写了一个简单的界面,你还可以用pyinstaller打包成exe文件,这样就可以和朋友们分享啦!

评论

Felix 管理员

2020博客第一篇文章

回复

  • 最新随笔

  • 尝试让DALLE生成一些连续的精灵图,让gpt帮忙生成一些提示词,如果能稳定输出的话就很强大了。
    让gpt帮忙生成的DALLE提示词
    "Generate a pixel art sprite sheet of a character walking in four directions (north, south, east, west) in a retro video game style."
    "Create a series of pixel art frames showing a character performing different actions like walking, running, jumping, and attacking in a classic 2D game aesthetic."
  • 路过别人山庄的门口,被一条大黑狗边叫边追过来,幸好骑电动车,不然还不一定跑得过,哈哈哈哈哈哈哈哈哈哈。
  • 最近两周也没咋出去玩,主要也是觉得没啥好玩的(笑哭)。看完布莱恩阿瑟的《复杂经济学》后,里面那个酒吧问题勾起我的兴趣,最近空了就花了些时间实现个python版本,顺便搞了篇博文,很享受这种新知识能和已有知识碰撞的感觉。(配张前段时间拍的图片,梧桐山门口前面那条路,挺漂亮的)
  • 盐田港夜景
  • 为啥这猫总喜欢喝杯子里的水
  • 确实开始冷了,在树林里至少要比人类聚集区低个几度,进出入口就能很明显感觉到。看看深圳水库的风景,貌似后面的视野更开阔。
  • 给随笔加了多图的功能,传一传周末拍的风景图,漫无目的的逛也挺好玩的。
  • 逻辑自洽是一套体系的根基,最根本的因素,最吸引人的地方