如何返回与图像部分最相似的Unicode字符?

9

我用Python做了一个简单的转换器,可以将图像转换为ASCII码。目前它使用各种暗色字符,所以它能够工作,但在低分辨率下很难辨认出来,例如:谷歌标志变成了:

  ..  .;.                  .@      
 a;       .. .;     . .. a. @ ...;.
 aa    .a.▒.  ▒.;.  ;.;; a. ▒ @a   
  .;.. .; .....  .    ..;;; ;  ;.. 
                     .a. .;        

这个几乎看不清。有没有一种方法可以将每个部分与Unicode字符的子集进行比较,并返回最相似的内容,例如返回类似以下的结果:

  ./--.\.                  /▒      
 a;       ./-.;  /  \ ./ \\ ▒ ./━\.
 aa    -a.▒.  ▒.|.  |.;▒  ┃ ▒ ▒-~┘ 
  \;.. /| \\_//  \ /  .\;;; ▒ \\.- 
                     .pp--▒  
3个回答

4
  1. 为要使用的每个字符生成一个图像,使用将要使用的字体。您可能会使用等宽字体,这样就可以创建一个大图像并在后期将其拆分。这可能就像在编辑器中键入字符并进行屏幕截图那么容易。
  2. 对于输入图像的每个补丁,将该补丁与所有字符图像进行比较。我会从补丁和字符中获取相应的像素,并将差的平方相加 - 具有最低总和的字符是最接近补丁的字符。

您可以通过对字符图像、输入图像或两者都进行模糊处理来改善结果。您还可以通过增加输入图像的对比度来获得更好的结果。

另一个提高结果质量和速度的想法是计算每个字符的平均黑度,并仅尝试匹配与补丁几乎相同黑度的字符。


1
有没有一种方法可以直接使用字体而不是屏幕截图?如果我理解正确,在UTF-8中大约有65,536个字符。 - Skyler
2
@user677624 并非所有的UTF-8字符都是可打印的或者单独使用的,很少有字体包含除欧洲字符以外的其他字符。当你在问题中提到“子集”时,我认为你会将其缩减到合理的数量。我知道PIL可以生成文本图像,但我还没有弄清楚如何与系统字体一起使用它。 - Mark Ransom
1
我想要包括形状绘图字符,以及普通字符,这样可以更准确地表示它。 - Skyler
马克:大多数(严谨的)字体实际上应该包含至少拉丁文、希腊文和西里尔文,以及各种变音符号、标点、箭头和图案。 - Joey
我希望能找到一篇文章,描述了这种类型的算法,它可以接收一个位图字符列表,然后将每个图像块与最相似的字符形状进行匹配。对于线条艺术效果非常好。 - Raynet

3

这是一个旧帖子,但我可以在这里添加我的解决方案。您可以使用盲文字符来获得像素级的表示。就像这样:

⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡿⡻⡫⡫⡣⣣⢣⢇⢧⢫⢻⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡟⡟⣝⣜⠼⠼⢚⢚⢚⠓⠷⣧⣇⠧⡳⡱⣻⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡟⣏⡧⠧⠓⠍⡂⡂⠅⠌⠄⠄⠄⡁⠢⡈⣷⡹⡸⣪⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⠿⢿⢿⢿⢟⢏⡧⠗⡙⡐⡐⣌⢬⣒⣖⣼⣼⣸⢸⢐⢁⠂⡐⢰⡏⣎⢮⣾⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣽⣾⣶⣿⢿⢻⡱⢕⠋⢅⠢⠱⢼⣾⣾⣿⣿⣿⣿⣿⣿⣿⡇⡇⠢⢁⢂⡯⡪⣪⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⠏⢎⠪⠨⡐⠔⠁⠁⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⢱⠡⡁⣢⢏⢮⣾⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢍⢆⢃⢑⠤⠑⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣿⣿⡿⡱⢑⢐⢼⢱⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⢫⡱⢊⢂⢢⠢⡃⠌⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⢟⢑⢌⢦⢫⣪⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡻⡱⡑⢅⢢⣢⣳⢱⢑⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡑⡑⡴⡹⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢝⠜⠨⡐⣴⣵⣿⣗⡧⡣⠢⢈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣜⢎⣷⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡫⡱⠑⡁⣌⣮⣾⣿⣿⣿⣟⡮⡪⡪⡐⠠⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⢏⠜⠌⠄⣕⣼⣿⣿⣿⣿⣿⣿⣯⡯⣎⢖⠌⠌⠄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢨⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢕⠕⢁⠡⣸⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⡽⡮⡪⡪⠨⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢕⠕⢁⢐⢔⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢽⡱⡱⡑⡠⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢕⠕⢁⢐⢰⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⣞⢜⠔⢄⠡⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⡿⡹⡰⠃⢈⠠⣢⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡮⣇⢏⢂⠢⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⢫⢒⡜⠐⠀⢢⣱⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣳⢕⢕⠌⠄⡀⠀⠀⢀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⡿⡑⣅⠗⠀⡀⣥⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⢙⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣯⢮⡪⣂⣢⣬⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⡟⡜⢌⡞⡀⣡⣾⣿⣿⣿⣿⣿⣿⣿⡿⠛⠉⢀⡠⠔⢜⣱⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⡿⡸⡘⢜⣧⣾⣿⣿⣿⣿⣿⣿⠿⢛⡡⠤⡒⢪⣑⣬⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⡇⡇⡣⣷⣿⣿⣿⣿⣿⠿⡛⡣⡋⣕⣬⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣮⣺⣿⣿⣟⣻⣩⣢⣵⣾⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿

我用Go语言编写了一个名为dotmatrix的工具,用于此目的:https://github.com/kevin-cantwell/dotmatrix

谢谢您的发布!即使这是一个旧帖子,但正是我正在寻找的。 - Shawn Presser

2
当你说“将每个部分与Unicode的子集进行比较”时,这并不是很清楚,因为有多种方法可以实现。我建议将比较降到像素级别。在灰度图像中,每个像素都有一个灰度值。假设您想用适当的字符替换每个像素,那么该字符应该如何匹配该像素?如果您从远处看一个字符,您只会看到一个灰色斑点。如果您现在用一个字符替换一个像素,则应选择与该像素最相似的灰度值的字符。
在等宽字体中,每个字符使用相同的空间量。如果您现在取此空间的矩形,并在其上绘制字符,则可以计算平均灰度值。这个平均灰度值不超过矩形中白色区域相对于整个矩形的面积。一个空格的灰度值为1。也许美元符号是您会发现最黑的字符之一。
所以这里是我会做的:
1. 取一组字符,无论您是否只使用ascii或uni-code。为每个字符计算白色的数量。显然,这可能对不同的字体不同,但您必须使用等宽字体。 2. 现在您有一个将每个字符映射到灰度值的列表。您现在应将灰度值重新缩放到目标灰度值间隔。如果您有一个8位图像,那么最亮的字符(空格)应对应值255,而最暗的字符应对应灰度级0。 3. 现在,重新调整您的输入图像,使其不要太大,因为即使使用非常小的字体,您也可能不会在一行上获得2000个字符。 4. 用灰度级最接近自己的灰度级的字符替换每个像素。
在Mathematica中,这只需要几行代码。在Python中它可能会有点长,但也应该可以。
使用这种方法,当您从远处观察文本时,您会得到非常惊人的结果,当您靠近时,您会发现它全部由字符组成。
更新
当您想创建与原始大小相同的图像时,方法并没有太大区别,但是即使在此处,如Mark所指出的那样,您也必须创建每个使用的字母的光栅图像。我真的看不出比较您的图像瓷砖与字母以决定哪一个最合适的更快的方法。
也许有一个提示:如果您使用这种方法,字母将在您的图像中可见,因为当您使用12pt字体时,每个字母至少具有大约10x15的图像大小。当您现在转换1000x1500的图像(这不是很小),您只使用100x100个字母。 因此,考虑使用图像渐变而不是图像本身可能值得一试。这样可以得到更好的图像,因为选择一个跟随边缘相当好的字母。 仅使用渐变,谷歌标志看起来并不那么糟糕。

1
是的,但我想生成与原始图像“相同大小”的ASCII艺术(请参阅我的第一篇帖子)-这种方法会创建绝对巨大的文本文件,无法在单个页面上显示。 - Skyler
这适用于中等大小的图像,但对于小图像而言,渐变比单个字符还要小。 - Skyler
这不是图片的问题,而是你如何光栅化每个字母的问题。你的字母表的光栅大小必须适合你使用的图像。 另外,如果你认为你的渐变太小了,为什么不膨胀它们呢? - halirutan
我无法更改字母光栅化的方式,因为它正在打印到终端。 - Skyler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接