乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      你不知道的詞云

       Gavin_Rainer 2019-06-25

      導讀

      Python123 特別策劃,教你如何像程序員和設計師一樣思考,探索技術(shù)背后的真相。

      一念之差,大有不同。上百小時的精心制作,只為給你帶來更易理解,更具深度的知識。

      PART 1

      - 當我看到詞云時,我在想什么?

      - 關(guān)鍵問題在哪?

      - 笨辦法

      - 動手做?

      - Wordcloud 探秘

      - 初窺圖像處理一角

      - 快速重疊檢測

      - 詞云布局方法

      - 著手實現(xiàn)

      - 明顯的缺點

      - 更好的策略

      PART 2

      - 四叉樹

      - 四叉樹重疊檢測

      - 矩形螺旋布局

      - 新的實現(xiàn)

      - 性能瓶頸

      - 優(yōu)化!優(yōu)化!

      - 為什么要實現(xiàn)一個新的詞云?

      - 更多字體

      - 更多顏色

      - 更多布局方法

      - 表情包云?

      - 你能做的更好!

      當我看到詞云時,我在想什么?

      當?shù)谝谎劭吹皆~云時,基于程序員的條件反射,一個大大的「How」字浮現(xiàn)在眼前:它是如何實現(xiàn)的?仔細觀察,又發(fā)現(xiàn)一些現(xiàn)象。詞語之間沒有重疊,詞語中間的空隙被更小的詞語填滿,有的詞語與詞語之間以奇怪的姿勢交錯在一起。考慮了一會兒,我知道我又一次進入了未知領(lǐng)域,以我所知的方法,似乎沒有一個能夠輕易解決這個問題。

      關(guān)鍵問題在哪?

      首先,在圖片上繪制文字肯定不是難事,雖然我沒用 python 畫過任何圖像,但如果這件事成為難點,那肯定是 python 生態(tài)出了問題。同樣的,設置文字為不同顏色或不同大小也應該不是難事。文字的位置、顏色、大小看起來都比較隨機,這也很簡單,random 庫就能實現(xiàn)。那最后就剩下一個問題,要如何做到詞語之間緊密排布但又沒有重疊呢?

      笨辦法

      假設先在一張白色的圖片 A 上放一個黑色的詞語 A,又在另一張同樣大小的白色的圖片 B 上放一個詞語 B,把這兩張圖片都打印到紙上,整齊的疊在一起,對著陽光看,不就能知道這兩個詞語有沒有重疊了嗎?如果重疊,就把詞語 B 換一個地方,重新打印對比;不重疊時,記下這個位置,詞語 B 打印到圖片 A 上……

      似乎不太困難,而且我知道我并不需要把圖片打印出來,可以讓 python 幫我完成對比的工作。

      圖片是由一個一個像素組成的,比如一張 800 × 600 的圖片,說明它是由 800 × 600 = 48萬 個像素組成。每個像素中保存了一個顏色值,通過對比圖片 A 和 B 的每一個像素,如果相同位置上,A 和 B 的像素都為黑色,那就意味這兩個詞語有重疊。

      雖然還有一些不清楚的地方,不過這確實是一個可行的方案。

      動手做?

      要開始用 python 動手實現(xiàn)了嗎?
      當然不,我們知道 python 有一個 wordcloud 庫可以生成詞云。

      那要開始介紹 wordcloud 庫嗎?
      是也不是。我更關(guān)心的是 wordcloud 庫如何解決我們的關(guān)鍵問題?

      Wordcloud 探秘

      從 Github 上可以輕易的下載到 wordcloud 庫的代碼。從入口函數(shù)開始,粗略的追蹤了幾個函數(shù)的調(diào)用,有幾個地方引起了我的注意:

      1. 它使用了 Pillow 庫,搜索發(fā)現(xiàn)這是一個 python 圖像處理庫。

      2. 它在一個 for word, freq in frequencies 循環(huán)中,將單詞逐個放到圖片上。

      3. 這個循環(huán)中,除了對字體進行設置外,還調(diào)用了一個 sample_position 方法來尋找放置的位置。<- 這就是我們所要的

      4. sample_position 很簡單,它的實現(xiàn)在 query_integral_image.pyx 文件中,僅有 30 多行,它從圖片左上角第一個像素開始,遍歷所有像素,檢查各個位置能否放置。另外,integral_image 翻譯為 積分圖。

      初窺圖像處理一角

      積分圖?聽起來似乎很復雜,但理解起來卻特別簡單。首先,積分圖不是一張真實的圖片,接下來……看圖吧:

      積分圖中,每個單元的值,等于原圖此位置左上角所有像素值之和(橙框的值 = 藍框中所有值之和)。這個性質(zhì),能夠幫助我們快速判斷一個區(qū)域內(nèi)有沒有內(nèi)容。為什么這么說呢?

      快速重疊檢測

      如果一個矩形區(qū)域內(nèi)沒有內(nèi)容,說明這個區(qū)域內(nèi)所有像素值之和為零。根據(jù)積分圖的特征,我們可以進行如下計算:用大矩形所有像素值之和,減去上方和左側(cè)兩個矩形像素值之和,再加上左上角小矩形像素值之和,就得到了所求區(qū)域內(nèi)像素值之和。


      這樣,只需要進行 四次取值和一次運算 就能夠判斷某區(qū)域是否為空,比逐個像素檢測快很多。

      每個詞語都能夠被框在一個矩形中(寬度為 w,高度為 h),我們只需要對圖片每個位置 (x,y) 進行計算,如果 (x,y) 到 (x + w - 1, y + h - 1) 這個矩形區(qū)域內(nèi)沒有內(nèi)容,就能夠放置這個詞語。

      至此,我們知道了詞云的第一個關(guān)鍵點:重疊檢測方法。

      詞云布局方法

      我們已經(jīng)能夠判斷新放置的詞語是否和其它詞語重疊,接下來的問題就是選擇一種策略來放置新詞語。

      wordcloud 庫使用了一個非常簡單的布局方法:

      1. 從左上角第一個像素開始,判斷在這個位置放置新詞語會不會與已放置的內(nèi)容重疊;

      2. 如果不重疊,將這個位置添加一個列表 list 中;

      3. 遍歷圖片所有像素,將所有可放置的位置都加到 list 中;

      4. 從 list 中隨機選擇一個位置放置這個詞語。

      這樣的方法推翻了我們之前的觀測,wordcloud 并沒有做任何工作來保證詞語之間緊密排布。但這并不困難,只要我們換一種方法:

      1. 選擇一個初始位置 P;

      2. 對每一個單詞,從位置 P 開始,稍微移動一點點距離,看能不能放下,如果不能,再稍微移動一點距離,直到能夠放下。

      不錯,就是簡單的貪心策略。

      著手實現(xiàn)

      import random

      import math

      from PIL import Image

      from PIL import ImageDraw

      from PIL import ImageFont

      words = ['發(fā)展', '中國', '人民', '建設', '社會主義', '堅持', '黨', '全面', '國家', '實現(xiàn)', '制度', '推進', '特色', '社會']

      # 創(chuàng)建一張 800×600 的圖片

      img = Image.new("L", (800, 600))

      draw = ImageDraw.Draw(img)

      for word in words:

        # 選擇字體和大?。汉隗w,大小隨機

        font_size = random.randint(50,150)

        font = ImageFont.truetype('C:\Windows\Fonts\SIMHEI.TTF', font_size)

        # 計算文字矩形框的大小

        box_size = draw.textsize(word, font=font)

        # 在圖片中尋找一個能放下矩形框的位置

        x, y = find_position(img, box_size[0], box_size[1])

        if x:

          # 找到一個位置,并繪制上文字

          draw.text((x, y), word, fill="white", font=font)

      img.show()

      關(guān)鍵的確定位置方法:

      import numpy as np

      def find_position(img, size_x, size_y):

        # 計算積分圖,cumsum([1,2,3,4]) => [1,3,6,10]

        integral = np.cumsum(np.cumsum(np.asarray(img), axis=1), axis=0)

        # 使用 wordcloud 的布局策略

        for x in range(1, 800 - size_x):

          for y in range(1, 600 - size_y):

            # 檢測矩形區(qū)域內(nèi)容是否為空

            area = integral[y - 1, x - 1] + integral[y + size_y - 1, x + size_x - 1]

            area -= integral[y - 1, x + size_x - 1] + integral[y + size_y - 1, x - 1]

            if not area:

              return x, y

        return None, None

      至此,我們已經(jīng)理解 wordcloud 生成詞云的基本原理。

      明顯的缺點

      當詞語數(shù)量增多時,運行代碼,久久未能得出結(jié)果。我意識到生成詞云是一個需要極大計算量的工作。

      wordcloud 使用了很多使代碼變得難以理解的優(yōu)化策略,并且將 find_position 函數(shù)轉(zhuǎn)用 Cython 實現(xiàn)(轉(zhuǎn)換為 C 語言)也在很大程度上說明了這個問題。

      速度問題暫且不提,畢竟生成詞云不是我的日常工作。但是通過實現(xiàn)上面的算法,我還發(fā)現(xiàn)另一個問題:矩形檢測的積分圖算法似乎不能很好的支持文字的旋轉(zhuǎn)。

      這件事很好理解,如果將一個單詞旋轉(zhuǎn)一定角度,那么它的外接矩形面積必然會比不旋轉(zhuǎn)時候大,這就需要更大的矩形區(qū)域來放置這個詞語,導致很多實際可以放置的位置卻不能放置。

      那么,有沒有更好的策略呢?

      更好的策略

      答案是肯定的,包含旋轉(zhuǎn)文字的詞云圖片并不鮮見。經(jīng)過一段時間的調(diào)查,我們發(fā)現(xiàn),在其它一些語言實現(xiàn)的詞云中,使用了不一樣的實現(xiàn)方式。

      四叉樹

      在 Wordcloud 算法中,每放置完一個詞語,就需要重新計算一次積分圖,下一個詞語需要與整張圖片進行重疊檢測。有沒有可能將每個詞視為單獨的實體,在放置新詞時,檢測它與其它每一個詞語有沒有重疊?

      在 Jonathan Feinberg 的中,介紹了一種叫做 層次邊界框(Hierarchical bounding boxes)的方法來快速實現(xiàn)兩個詞語間的重疊檢測。

      層次邊界框這個詞太拗口,我們換用「四叉樹」來代指這種結(jié)構(gòu),它本質(zhì)上也是一棵記錄空間信息的四叉樹。

      四叉樹的構(gòu)建并不困難,將圖片橫縱各切一刀,平均分割為「左上、左下、右上、右下」四個區(qū)域,如果某個區(qū)域中有內(nèi)容(此時可以用積分圖算法判斷),那么繼續(xù)將這個區(qū)域分割為四個部分,直到區(qū)域的大小小于某個值。







      四叉樹每深一層,對形狀的描述就越精確,每一次分隔,都能排除一些空白矩形區(qū)域,剩下的有像素的區(qū)域,都記錄到了樹中。

      class QuadTree:

      x1 = 0

      y1 = 0

      x2 = 0

      y2 = 0

      children = None

      def __init__(self, x1, y1, x2, y2):

        self.x1,self.y1,self.x2,self.y2 = x1,y1,x2,y2

      # 計算積分圖

      integral = np.cumsum(np.cumsum(np.asarray(sprite.img), axis=1), axis=0)

      def build_tree_recursive(x1, y1, x2, y2):

        area = integral[y1 - 1, x1 - 1] + integral[y2, x2]

        area -= integral[y1 - 1, x2] + integral[y2, x - 1]

          if not area:

            # 區(qū)域內(nèi)沒有像素

            return None

        # 區(qū)域內(nèi)有像素,繼續(xù)劃分

        children = []

        cx = int((x1 + x2) / 2)

        cy = int((y1 + y2) / 2)

        tree = QuadTree(x1,y1,x2,y2)

        # 最小矩形區(qū)域邊長為 1 像素

        if x2 - x1 > 1 or y2 - y1 > 1:

          c0 = build_tree_recursive(x1, y1, cx, cy)  # 左上區(qū)域

          c1 = build_tree_recursive(cx,  y1, x2, cy) # 右上區(qū)域

          c2 = build_tree_recursive(x1, cy, cx, y2)  # 左下區(qū)域

          c3 = build_tree_recursive(cx, cy, x2, y2)  # 右下區(qū)域

          if c0:

            children.append(c0)

          if c1:

            children.append(c1)

          if c2:

            children.append(c2)

          if c3:

            children.append(c3)

          if len(children):

            tree.children = children

        return tree

      四叉樹重疊檢測

      那么,如何使用四叉樹檢測單詞是否重疊呢?

      首先,通過搜索我們知道,檢測兩個矩形是否重疊非常容易,只要知道兩個矩形的坐標,通過簡單的公式就可以計算。

      對于兩個單詞,首先可以構(gòu)建兩棵四叉樹。判斷四叉樹第一層的矩形是否有重疊,如果沒有,說明兩個單詞不可能重疊;如果有,說明兩個單詞可能重疊,繼續(xù)檢測下一層中記錄的區(qū)域。

      最終,要確定兩個單詞重疊,需要判斷兩個四叉樹的各葉子結(jié)點中,至少有兩個重疊。當然,直接進行葉子結(jié)點的遍歷也是可行的,但逐層判斷能省掉大量時間。

      # (x1,y1) 為 tree 對應單詞放置位置,(x2,y2) 為 otherTree 對應單詞放置位置

      def overlaps(tree, other_tree, x1, y1, x2, y2):

        if rectCollide(tree, other_tree, x1, y1, x2, y2):

          if not tree.children:

            if not other_tree.children:

              return True

            else:

              for i in range(0, len(other_tree.children)):

                if overlaps(tree, other_tree.children[i], x1, y1, x2, y2):

                  return True

          else:

            for i in range(0, len(tree.children)):

              if overlaps(other_tree, tree.children[i], x2, y2, x1, y1):

                return True

        return False

      # 位于 (x1,y1) 的四叉樹中矩形 a 和位于 (x2,y2) 的四叉樹中矩形 b 是否重疊

      # (a.x1,a.y1) 是 a 在矩形樹中的相對坐標

      # (a.x1 + x1, a.y1 + y1) 是矩形 a 在圖像中的坐標

      def rect_collide(a, b, x1, y1, x2, y2):

        return y1 + a.y2 > y2 + b.y1 and y1 + a.y1 < y2 + b.y2 and x1 + a.x2 > x2 + b.x1 and x1 + a.x1 < x2 + b.x2

      矩形螺旋布局

      除了使用新的重疊檢測算法,我們還打算引入一個新的布局方法來使最終的詞云更好看。


      矩形螺旋布局也是一種貪心布局策略,所有的單詞緊密放置在圖中心的螺旋形狀上。每個單詞都從螺旋的中心位置開始檢測,如果不能放置,就移動到螺旋的下一個位置。

      # 參數(shù)為圖像的大小

      def rectangular_sprial(width, height):

        dy = dx = 10

        x = y = 0

        def sprial(t):

          nonlocal x, y

          num = int(math.sqrt(1 + 4 * t) - 1) & 3

          if num == 0:

            x += dx

          elif num == 1:

            y += dy

          elif num == 2:

            x -= dx

          else:

            y -= dy

        return x + (width) // 2, y + (height) // 2

        # 返回一個閉包

        return sprial

      新的實現(xiàn)

      我們已經(jīng)有充足的知識來重新實現(xiàn)一個詞云了,順便實現(xiàn)文字以隨機角度旋轉(zhuǎn)布局。

      (完整代碼見文末)

      性能瓶頸

      詞云的生成速度比積分圖算法稍快,但仍然需耐心等待。

      因為每個單詞都需要與其它所有單詞進行重疊檢測,導致 N2 的時間復雜度,單詞數(shù)量增多時性能會急劇下降。

      由于圖像中心文字密集,需要檢測大量無效位置后才能確定放置地點(通常只能放在圖像的外側(cè)邊緣)。

      優(yōu)化!優(yōu)化!

      為了使詞云運行速度達到可以接受的程度,我們使用兩種簡單策略來優(yōu)化詞云算法。

      一種策略的想法來源于四叉樹。先把圖像分割為多個矩形區(qū)域,比如 800×600 的圖片,分割為 48(8×6)個區(qū)域,每個區(qū)域長寬都是 100 像素。布局每個單詞后,就將這個單詞添加到對應的區(qū)域中,這樣在布局一個新的單詞時,只要根據(jù)單詞大概的大小和放置的位置,在有限的幾個區(qū)域中進行重疊檢測,大幅降低了需要進行重疊檢測的次數(shù)。

      第二種策略更多來源于經(jīng)驗。假如要放置數(shù)十個大小差不多的單詞,第一個單詞檢測了螺旋上的 200 個坐標后,找到一個放置位置,那么下一個單詞就跳過這 200 個坐標(因為大概率重疊),在第 300 個坐標上放置,下一個單詞從第 300 個坐標開始……如果檢測到了圖片邊界未找到可以放置的坐標,那么回到初始位置重新尋找。

      為什么要實現(xiàn)一個新的詞云?

      為了更多的可能性。

      更多字體

      作為前端開發(fā)者,我們經(jīng)常使用 fontawesome 圖標字體,那么用它來制作詞云如何?

      更多顏色

      詞云庫多數(shù)都使用隨機顏色,但是懂得了實現(xiàn)原理,顏色可以更有意義:

      1. 按詞頻設置單詞顏色。

      2. 讀取另一張色彩豐富的圖片作為顏色映射。

      3. 對不同區(qū)域使用不用顏色。

      更多布局方法

      在網(wǎng)絡上的詞云圖中,我們經(jīng)常見到把所有詞語放置在一定區(qū)域內(nèi)的圖片。

      要實現(xiàn)起來也很簡單,事先制作一張黑白圖片作為遮罩,當作單詞計算四叉樹后,放到圖片中即可。單詞除了與其它單詞進行重疊檢測外,也與遮罩進行重疊檢測。

      使用多個遮罩也不是難事,甚至可以使用代碼繪制遮罩。

      除此之外,你還可以:

      1. 讓詞語都平行或斜向排布。

      2. 固定一些詞語的大小、位置和顏色,讓重點更加突出。

      例如頁面開頭的詞云。

      表情包云?

      聰明的你已經(jīng)意識到,詞云中的每個單詞其實都是當作圖片處理的,那么,是不是可以用詞云算法來布局圖片呢,比如表情包?

      你能做的更好!

      嘗試修改 wordcloud.py,創(chuàng)作你自己的詞云!

      可能還需要學習的知識:

      1. 使用 Pillow 庫繪制和處理圖像

      2. 使用 numpy 庫進行數(shù)學運算

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多