字符画生成

字符画生成的种类

  • 生成图片:文字大小不一样大,像词云一样
  • 生成文字:纯文本

图像的种类:

  • 黑白
  • 灰度图

之前用Java实现过一个用字符写字符,那个只能画出黑白图像。本文用python PIL库实现纯文本字符画,它能够用字符画出灰度图像。

原理如下:

  • 首先定义一个字符集,本程序使用ASCII码中的可打印字符:32~126
  • 其次,每一个字符都对应一个灰度值,不同字符灰度值不同,具体如何计算一个字符的灰度值见下文
  • 传入一张彩色图片,先对它进行灰度化、放缩处理(能够使得宽度合适,每行字符串避免太长)、直方图均衡化(使得图像灰度均匀,增强对比度)。经过以上步骤,得到一个灰度数组。
  • 根据图像的灰度数组和字符的灰度值,将灰度数组映射为字符串

如何计算一个字符的灰度值?
将字符画在一张纸上,统计这个字符所占的面积,面积越大,说明字符灰度值越大(或者恰恰相反也是可以的)。

关于直方图均衡化,请查看直方图均衡化

本程序可以进行如下配置:

  • 更改字符集,可以包含汉字
  • 更改导出图片的字体、字符间距

先放上一张大大的帅照:

695653-20180924015141292-317909583.png

from PIL import ImageFont, Image, ImageDraw# 字符集使用ascii码中的可打印字符
charset = [chr(i) for i in range(32, 127)]
# 计算字符灰度时,字体使用默认字体
font = ImageFont.load_default()def histogram(a):# 统计各个颜色出现的频率cnt = [0] * 256for i in a:cnt[i] += 1return cntdef transform(a):# 为各个颜色赋予新的颜色值su = sum(a)ans = [0] * 256s = 0for i in range(len(a)):s += a[i]ans[i] = int(255 * s / su)return ansdef map_by(a, b):# 根据映射b,将a数组中的元素映射为新的数组ans = []for i in a:ans.append(b[i])return ansdef get_grey(char):# 获取单个字符的灰度sz = font.getsize(char)img = Image.new('1', sz)draw = ImageDraw.Draw(img)draw.text((0, 0), char, fill='white')white_cnt = 0for i in range(sz[0]):for j in range(sz[1]):if img.getpixel((i, j)):white_cnt += 1return white_cnt / (sz[0] * sz[1])def get_charset_grey():# 获取字符集中各个字符的灰度charset_grey = []for i in charset:grey = get_grey(i)charset_grey.append((i, grey))charset_grey = sorted(charset_grey, key=lambda it: it[1])max_grey = charset_grey[-1][1]  # 最大灰度的字符charset_grey = list(map(lambda it: (it[0], it[1] / max_grey * 255), charset_grey))return charset_greydef near(a, x):# 根据灰度x在“字符-灰度”列表中查找灰度最接近的字符,此处使用二分查找lo, hi = 0, len(a) - 1while lo < hi:mid = (hi + lo) // 2if a[mid][1] == x:return a[mid][0]elif a[mid][1] < x:lo = mid + 1else:hi = midind = loif ind == 0: return a[0][0]if abs(a[ind][1] - x) < abs(a[ind + 1][1] - x):return a[ind][0]else:return a[ind + 1][0]def draw_char(charset_grey, img_data):# 根据“字符-灰度”列表将图像数据映射成字符串s = ""for i in img_data:s += near(charset_grey, i)return sdef char_image(img_path, line_chars=100):# 传入图片路径,将图片映射成为字符串# 首先将原图片进行灰度化、放缩、直方图均衡化img = Image.open(img_path).convert('L')height = int(line_chars / img.size[0] * img.size[1])img = img.resize((line_chars, height))data = list(img.getdata())new_data = map_by(data, transform(histogram(data)))charset_grey = get_charset_grey()s = draw_char(charset_grey, new_data)s = '\n'.join([s[i * img.size[0]:(i + 1) * img.size[0]] for i in range(img.size[1])])return sdef toimg(s):# 将一个多行字符串画到图片上s = s.split('\n')ch_sz = font.getsize(' ')  # 先测试一下单字符宽高(以空格为例)ch_sz = (ch_sz[0] + 2, ch_sz[1] + 2)  # 字符之间空闲两格img = Image.new('1', (ch_sz[0] * len(s[0]), ch_sz[1] * len(s)))  # 创建新图片draw = ImageDraw.Draw(img)for i in range(len(s)):for j in range(len(s[0])):draw.text((j * ch_sz[0], i * ch_sz[1]), s[i][j], fill='white')return imgs = char_image("bitch.jpg", line_chars=200)
img = toimg(s)
img.save("haha.jpg")
print(s)


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部