数学公式转图片:纯Python实现,可设置字体、字号、颜色和分辨率

开发 后端
写数学公式,功能最强大的当然是LaTex了。不过,强大不代表易用,驾驭LaTex绝不是一件容易的事儿。这也不难理解:毕竟数学公式不是孤立存在的,必然要作为文档、网页或者程序输出的元素,如何无缝地让LaTex关联到文档、网页或程序,的确是个棘手的难题。

[[407455]]

写数学公式,功能最强大的当然是LaTex了。不过,强大不代表易用,驾驭LaTex绝不是一件容易的事儿。这也不难理解:毕竟数学公式不是孤立存在的,必然要作为文档、网页或者程序输出的元素,如何无缝地让LaTex关联到文档、网页或程序,的确是个棘手的难题。

既然直接使用LaTex有难度,那就退而求其次:借助于工具将数学公式转为图片,然后就可以方便地应用到文档、网页或者程序中了。这样的工具,除了在线式的,基本上都是重量级的,安装和使用极不方便。我曾经花了11个C币从CSDN买过一个数学公式转图片的工具,下载之后发现,竟然只是封装了一个http请求,图片仍然是在线生成的!一气之下,自己写了个离线的,只是功能比较简单,不能方便地设置输出图像的大小和颜色。

最近工作中又用到了LaTex,几经尝试,终于在matplotlib源码中发现了一个处理LaTex数学公式的好东西。稍加改造,增加了字体、字号、颜色和分辨率的设置,最终完美解决了LaTex数学公式转图片的问题。

1. 核心代码

先从最简单的开始吧。matplotlib有个mathtext子模块,提供了math_to_image函数可以直接将LaTex数学公式生成图片。下面的代码,仅仅两行,就将质能方程转成了图片。请注意,LaTex数学公式一定要包含在两个$符号之间。

  1. >>> from matplotlib import mathtext 
  2. >>> mathtext.math_to_image(r'$E=mc^2$', r'd:\demo_1.png'

生成的图片宽度55个像素,高度15个像素,分辨率为100dpi。这么迷你,看上去可怜巴巴的。

虽然指定了png格式,也确实存在透明通道,其背景却是不透明的。除了png格式外,math_to_image函数还支持eps, pdf, pgf, png, ps, raw, rgba, svg, svgz等格式,但不支持jpg格式。

2. 设置字体、字号、分辨率

要设置字体字号,就得先导入matplotlib的font_manager字体管理模块。该模块的FontProperties类可以实例化一个字体对象传给math_to_image函数,用来设置family(字体)、size(字号)和weight(笔画轻重)等。

math_to_image函数的dpi参数用于设置分辨率(每英寸像素数)。如果应用于网页的话,建议分辨率设置为72dpi就可以了,如果用于印刷,请将dpi设置为300。

  1. >>> import matplotlib.font_manager as mfm 
  2. >>> prop = mfm.FontProperties(family='sans-serif'size=64, weight='normal'
  3. >>> mathtext.math_to_image(r'$E=mc^2$', r'd:\demo_2.png', prop=prop, dpi=72) 

输出结果如下。这次生成的图片终于不那么可怜了,宽度250个像素,高度59个像素。

如果不知道有哪些字体可用怎么办?不要担心,下面这一行代码就可以列出当前系统中全部的可用字体。在我的电脑上运行之后,找到了几百种可用的字体(重名的字体表示该字体有多个字体文件)。

  1. >>> [item.name for item in mfm.fontManager.ttflist] 

3. 设置颜色

要想对图片文件做颜色处理,最好的方式是先将math_to_image的输出暂存到类文件对象中,借助于PIL和NumPy完成颜色设置后,再保存为文件。为此,要先导入io模块、pillow模块和numpy模块。

在开始写代码前,先约定使用浮点型的三元组表示颜色,比如,(0.17, 0.63, 0.17)表示亮度稍暗的绿色。如果喜欢使用其他方式表示颜色,请自行转换。下面的例子换了一个复杂的数学公式(虚构的,并无实际意义)来演示如何设置颜色。

  1. >>> from io import BytesIO 
  2. >>> from PIL import Image 
  3. >>> import numpy as np 
  4. >>> text = r'$s(t) = \mathcal{A}\mathrm{sin}(2 \omega \sum_{i=0}^\infty t_i)$' 
  5. >>> color = (0.17, 0.63, 0.17) # 要使用的颜色 
  6. >>> bfo = BytesIO() # 创建二进制的类文件对象 
  7. >>> prop = mfm.FontProperties(family='Palatino Linotype'size=256, weight='normal'
  8. >>> mathtext.math_to_image(text, bfo, prop=prop, dpi=72) 
  9. 209.0 
  10. >>> im = Image.open(bfo) # 打开二进制的类文件对象,返回一个PIL图像对象 
  11. >>> r, g, b, a = im.split() # 分离出RGBA四个通道 
  12. >>> r, g, b = 255-np.array(r), 255-np.array(g), 255-np.array(b) # RGB通道反白 
  13. >>> a = r/3 + g/3 + b/3 # 生成新的alpha通道 
  14. >>> r, g, b = r*color[0], g*color[1], b*color[2] # RGB通道设置为目标颜色 
  15. >>> im = np.dstack((r,g,b,a)).astype(np.uint8) # RGBA四个通道合并为三维的numpy数组 
  16. >>> im = Image.fromarray(im) # numpy数组转PIL图像对象 
  17. >>> im.save(r'd:\demo_3.png') # PIL图像对象保存为文件 

来看看最终的输出结果是什么样的呢?最终生成了2451x653的一张大图,公式内容、字体、字号、颜色等,正如期望的那样。大功告成!

4. 封装成函数

为了方便使用,将上面的代码封装成一个函数,完整代码如下。

 

  1. # -*- coding: utf-8 -*- 
  2.  
  3. import os 
  4. from io import BytesIO 
  5. from PIL import Image 
  6. import numpy as np 
  7. import matplotlib.font_manager as mfm 
  8. from matplotlib import mathtext 
  9.  
  10. def latex2img(text, size=32, color=(0.1,0.1,0.1), out=None, **kwds): 
  11.     """LaTex数学公式转图片 
  12.  
  13.         text        - 文本字符串,其中数学公式须包含在两个$符号之间 
  14.         size        - 字号,整型,默认32 
  15.         color       - 颜色,浮点型三元组,值域范围[0,1],默认深黑色 
  16.         out         - 文件名,仅支持后缀名为.png的文件名。若为None,则返回PIL图像对象 
  17.         kwds        - 关键字参数 
  18.                         dpi         - 输出分辨率(每英寸像素数),默认72 
  19.                         family      - 系统支持的字体,None表示当前默认的字体 
  20.                         weight      - 笔画轻重,可选项包括:normal(默认)、light和bold 
  21.         ""
  22.  
  23.     assert out is None or os.path.splitext(out)[1].lower() == '.png''仅支持后缀名为.png的文件名' 
  24.  
  25.     for key in kwds: 
  26.         if key not in ['dpi''family''weight']: 
  27.             raise KeyError('不支持的关键字参数:%s'%key
  28.  
  29.     dpi = kwds.get('dpi', 72) 
  30.     family = kwds.get('family', None) 
  31.     weight = kwds.get('weight''normal'
  32.  
  33.     bfo = BytesIO() # 创建二进制的类文件对象 
  34.     prop = mfm.FontProperties(family=family, size=size, weight=weight) 
  35.     mathtext.math_to_image(text, bfo, prop=prop, dpi=dpi) 
  36.     im = Image.open(bfo) 
  37.  
  38.     r, g, b, a = im.split() 
  39.     r, g, b = 255-np.array(r), 255-np.array(g), 255-np.array(b) 
  40.     a = r/3 + g/3 + b/3 
  41.     r, g, b = r*color[0], g*color[1], b*color[2] 
  42.  
  43.     im = np.dstack((r,g,b,a)).astype(np.uint8) 
  44.     im = Image.fromarray(im) 
  45.  
  46.     if out is None: 
  47.         return im 
  48.     else
  49.         im.save(out
  50.         print('生成的图片已保存为%s'%out
  51.  
  52. if __name__  == '__main__'
  53.     text = r'$\sum_{i=0}^\infty x_i$' 
  54.     latex2img(text, size=48, color=(0.1,0.8,0.8), out=r'd:\demo.png'
  55.  
  56.     text = r'$\sum_{n=1}^\infty\frac{-e^{i\pi}}{2^n}$' 
  57.     im = latex2img(text, size=48, color=(0.9,0.1,0.1)) 
  58.     im.show() 

 

责任编辑:武晓燕 来源: Python作业辅导员
相关推荐

2017-04-27 08:19:56

Markdown数学公式

2017-06-13 12:57:42

Windows 10Windows分辨率

2023-08-30 13:09:12

AI模型

2014-08-07 15:06:45

Android设备分辨率

2017-04-10 09:17:08

神经网络分辨率像素递归

2014-10-15 14:58:50

iPhone6iOS 8分辨率

2013-08-13 11:09:10

android分辨率

2017-05-04 21:15:30

Android分辨率

2023-10-27 08:30:52

传输接口刷新率

2012-08-14 16:52:26

手机屏幕分辨率

2011-01-07 10:26:13

linux报警声分辨率

2011-08-16 17:57:51

linux修改分辨率

2024-08-27 00:01:00

LaTeX语言符号

2023-10-12 07:43:45

2009-04-16 08:19:40

Windows 7微软操作系统

2017-09-06 08:49:33

机器学习数学公式代数

2011-05-30 08:42:56

Android

2012-01-05 16:08:57

佳能激光打印机

2009-11-27 13:03:42

SUSE FAQ修改X

2024-07-05 10:41:30

目标检测算法
点赞
收藏

51CTO技术栈公众号