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

clipboard.png

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

我比较长怎么办啊?

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

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))

看看效果吧:github

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

clipboard.png

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

来作点小数据分析吧

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

# 准备分析数据
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轴,画出样本的散点图segmentfault

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

运行以后,会弹出这样一个窗口
clipboard.png
好的,从这个图片上分析,咱们的字号与宽度是一个完美的正相关,用函数来表示,就是数组

$$ y=kx+b $$app

那问题来了,咱们如何取得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)

clipboard.png
把拟合曲线也画在图标上:

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

clipboard.png
能够看到拟合曲线完美的通过了每个数据点,这基本就能够认定咱们的拟合曲线基本上就是 y=x了,
固然,咱们的样本量如今是很是少的,也很是的规整,其实更多状况下,数据多是这样分布的:
clipboard.png
这样是否是就能体现出拟合的意义了呢?

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

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

clipboard.png

学霸们,动起来!

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

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

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

由于做者数学水平太差了,咱们下期换个方向玩

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

相关文章
相关标签/搜索