【正经的AI on Python入门系列】1.2 斗图工具的优化——文本宽度自适应(来做点小数据分析吧)

《【正经的AI on Python入门系列】1.2 斗图工具的优化——文本宽度自适应(来做点小数据分析吧)》

上一篇文章【图工具的优化——实现文本居中】中,我们已经实现了对插入字体的左中右对齐显示,那因为上期文章混进去了不少语法讲解,所以后面的内容就顺延到这啦,哈哈哈。

我比较长怎么办啊?

我们的斗图小工具,现在面临这一个苦恼,这些文本他坏,一会长一会短的,一旦有个很长很长的,直接就捅到里面去了,根本显示不全啊,这咋办呢?
我稍微想了下,这个也简单,我可以不断的减小字号,直到我们的空白区域可以放得下:

    while (CONST_IMG_WIDTH <= textLen + 2*off_set[0]) and fontSize >= 1:
        fontSize -= 1
        imageFont = ImageFont.truetype('./resources/msyh.ttc', fontSize)
        textLen = draw.textsize(text, imageFont)[0]
        print("当前字号{},文本宽度{}".format(fontSize, textLen))

看看效果吧:

python emofigther.py 长的就会变细变细了就能塞下了嘛

《【正经的AI on Python入门系列】1.2 斗图工具的优化——文本宽度自适应(来做点小数据分析吧)》

效果其实还是挺好的,就是实现的方式有点太low了,而且不停的加载字体,看着就觉得开销很大,那有没有更优雅的办法呢?

来做点小数据分析吧

下面我们来研究一下,字体的字号大小跟其经过PIL绘制之后的大小有什么关系,接下来我们主要会用到Numpy、matplotlib跟scipy几个库。
先来准备点数据样本,通过draw.textSize函数,绘制单个字并获取其大小:

# 准备分析数据
font_num = []
text_size = []
for i in range(1, 31):
    imageFont = ImageFont.truetype('./resources/msyh.ttc', i)
    text_size.append(draw.textsize("字", font=imageFont))
    font_num.append(i)

借助matplotlib的pyplot模块,我们可以绘制各种图像,先让我们以字号为x轴,字体宽度为y轴,画出样本的散点图

import matplotlib.pyplot as plt
#....
# scatter画出散点图,以字号为x轴,字体宽度为y轴
# 在分析前,先绘制散点图,对大致的函数形状进行分析
plt.scatter(list(map(lambda x: x[0], text_size)), font_num, color="b", label=u"字体宽度")

运行之后,会弹出这样一个窗口
《【正经的AI on Python入门系列】1.2 斗图工具的优化——文本宽度自适应(来做点小数据分析吧)》
好的,从这个图片上分析,我们的字号与宽度是一个完美的正相关,用函数来表示,就是

$$ y=kx+b $$

那问题来了,我们如何取得k和b两个常数的值呢,那个说k=1,b=0的同学你坐下!我们要严谨,看出来了也不要说出来嘛,额,不对,就算是看出来了,但我们还是要以严谨的方式去证明他的!为了求出k和b两个常数的最优解,我们需要用到scipy.optimize模块的leastsq函数,这个函数实现了“最小二乘法”算法,通过不断的尝试不同的常数,求出与期望结果误差最小的最优解,那下面就简单介绍一下怎么用leastsq对函数进行拟合:

首先,我们要定义一个函数形状(一元一次、一元二次、多元多次)

def func_shape(p, x):
    """定义函数形状,哈哈哈,就是 y = kx+b 直线!
    Args:
        p: 常数
        x: 自变量
    Returns:
        函数运算求得的因变量
    """
    k,b = p
    return k*x + b

然后定义一个误差计算函数

def func_err(p, x, y):
    """定义误差函数
    Args:
        p: 常数
        x: 自变量
        y: 验证因变量
    Returns:
        返回函数运算结果与验证因变量之间的误差值
    """
    return func_shape(p, x) - y

使用leastsq函数进行求解,获取最优常量k、b

from scipy.optimize import leastsq

r = leastsq(func_err, p0, args=(_font_size_np[:,0], _font_num_np))
# 计算结果中的r[0]为一个元组,为求得的k和b
k, b = r[0]
# 最后我们得出结论,拟合结果为y = x
print("k=",k,"b=",b, "r=", r)

《【正经的AI on Python入门系列】1.2 斗图工具的优化——文本宽度自适应(来做点小数据分析吧)》
把拟合曲线也画在图标上:

# 画出拟合线,以字号为X轴,函数运算结果为Y轴
plt.plot(X,func_shape((k, b), X),color="orange",label=u"字体宽度拟合",linewidth=2) 

《【正经的AI on Python入门系列】1.2 斗图工具的优化——文本宽度自适应(来做点小数据分析吧)》
可以看到拟合曲线完美的经过了每一个数据点,这基本就可以认定我们的拟合曲线基本上就是 y=x了,
当然,我们的样本量现在是非常少的,也非常的规整,其实更多情况下,数据可能是这样分布的:
《【正经的AI on Python入门系列】1.2 斗图工具的优化——文本宽度自适应(来做点小数据分析吧)》
这样是不是就能体现出拟合的意义了呢?

让我们把研究成果用在我们的代码上:

    # 方法2:通过简单的数据分析,我们研究出字体宽度 = 字体字号这一函数
    def char_len(text_size):
        return text_size
    # 减小字号,直到 字数*单位宽度 适应空白区域宽度
    while char_len(fontSize) * len(text) > (CONST_IMG_WIDTH - 2*off_set[0]):
        fontSize -= 1

《【正经的AI on Python入门系列】1.2 斗图工具的优化——文本宽度自适应(来做点小数据分析吧)》

学霸们,动起来!

如果有小伙伴们看到这个章节,对本章节描述的数据分析过程非常感兴趣,而且觉得自己的数学功底非常扎实(特别是离散数学、概率、统计这方面的)你们请离开本系列文章——因为你们已经了解到了在科学计算领域,Python也是一把不错的兵刃,而你们,被选中的魔法少女(大雾)们,可以去深入了解以下几个库,然后投入到轰轰烈烈的数据分析事业中去吧!

  • Numpy —— 为Python提供了多维数组的扩展,同时也提供了丰富的集合运算、矩阵运算、向量运算,可以说是Python科学计算的基石
  • matplotlib —— 可产生出版物质量的图表的2D绘图库,数据可视化是数据分析不可或缺的手段之一
  • pandas —— 数据分析库,包括数据框架(dataframes)等结构
  • Scipy —— 高级科学计算库,提供了大量的科学计算工具及算法,例如本文用到的leastsq最小二乘法求解多项式算法(妈妈再也不用担心我要重复造轮子了!)

这些库的相关资料都非常的好找,而小弟又才疏学浅,就不再对它们在作过多展开了!

因为作者数学水平太差了,我们下期换个方向玩

按照惯例,放上此次的源码:
GitHub
其中的char_analysis.py即为本文所属的函数拟合例子

    原文作者:Akers
    原文地址: https://segmentfault.com/a/1190000015876390
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞