【正經的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即爲本文所屬的函數擬合例子

相關文章
相關標籤/搜索