背景
顾名思义,字符画就是由字符组成的“画”,效果如下图所示。
注: 可以点击页面右上角“文件目录”图标,选择src/step3/answer/step3_2.txt文件,查看字符画效果,如下图所示。
如下图所示,屏幕上显示的字符会占据一个矩形区域,若矩形区域为白色、字符为黑色,则当这个字符笔画较少时,区域看起来会比较“白”,反之则较“黑”,而这种“黑”“白”程度就形成了一种特殊的“灰度”。
一个字符所占的矩形区域可以看作是图像中的一个像素,则一个 m 行、每行 n 个字符的文本就相当于一个分辨率为 m×n 的“图像”,往这幅“图像”的“像素”中填入不同字符,就会使各个“像素”呈现出不同的“灰度”,从而呈现出“画”的效果,这便是字符画的原理。
要将一幅真彩色图像转换成字符画,可以先将其转换成灰度图,再将每个像素的灰度值转换成某个字符即可。现在的关键问题是如何将一个灰度值 gray 转换成对应字符,方法如下。
定义字符串:
@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`'
设其长度为 N。该字符串给出了字符画中所有可能出现的字符,且字符串中越靠前的字符越“黑”,越靠后的越“白”。因此,只要将灰度值的取值范围 [0,255] 与这 N 个字符建立对应关系,即可实现转换。实际上,对于灰度值gray,其对应的是字符串中第int(N*gray/256)号字符(这里的int函数起到向下取整的作用)。
任务
本关任务是补全程序,使其能将一幅真彩色图像转换成字符画,关于代码的相关说明见注释。
相关知识
本关程序的框架与之前关卡类似,但有以下几个方面需要说明。
1)如果图像的分辨率较高,则转换后的字符画有很多行,每行有很多字符,这会影响观看效果,所以初始程序首先对原始图像进行了缩放,将其宽度调整为w、高度调整为h,与之对应地,生成的字符画每行有w个字符,总共h行。
2)转换时,应采用“逐行处理”的方式(之前用的是“逐列处理”方式),即先依次转换第 0 行的所有像素,然后转换第 1 行,等等,所以嵌套循环应该采用如下形式:
……
for y in range(h): #对于第y行
for x in range(w): #对于第x列
……
)每行像素转换完后,应在字符串最后添加一个换行符’\n’,字符画才能形成多行的效果;
4)平台会自动调用toCharImage等函数,你只需补全函数,不需要调用;若要在自己电脑的 Python 环境中尝试本关程序,删除最后两行的注释。
编程要求
在 Begin-End 区间补全代码,具体要求见上。
测试说明
测试集正确结果如下:
0 -> @
50 -> b
100 -> U
150 -> /
200 -> +
255 -> '
你的字符画与正确答案相同!
说明如下:
1)系统会调用GraytoChar函数,并以“灰度值 -> 对应字符”的格式打印结果,如测试集第 1 行的 0 -> @表示灰度值0对应的字符是@;
2)此外,系统会检查程序生成的字符画,若正确则在测试集最后一行打印你的字符画与正确答案相同!。
开始你的任务吧,祝你成功!
from PIL import Image
# 将一个灰度值转换成对应的字符
def GraytoChar(gray):
allChar = """@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`'"""
########## Begin ##########
char = allChar[int(len(allChar)*gray/256)]
########## End ##########
return char
# 将真彩色图像转换成字符画,真彩色图像文件路径为path
def toCharImage(path):
img = Image.open(path)
ow, oh = img.size # 原始图像的宽和高
w = 80 # 调整后的宽度(即字符画每行有w个字符)
h = w * oh // ow # 高度也按比例调整(即字符画总共有h行)
h = h // 2 # 在文本中,行与行之间有间隔,要进一步缩小高度才能得到较好效果
img = img.resize((w, h)) # 调整图片大小
img = img.convert('L') # 将真彩色图像转换为灰度图
charImg = '' # 用于存储字符画
########## Begin ##########
# 将img中的灰度值逐一转换成字符,每得到一个字符,添加到charImg最后,转换完一行要添加换行符
for y in range(h):
s = ''
for x in range(w):
gray = img.getpixel((x, y)) # 取出灰度值
char = GraytoChar(gray)
s = s + char
charImg = charImg + s + '\n'
########## End ##########
return charImg
s = toCharImage('step3.jpg')
print(s)