背景
在這個世界裡,有一群人,
他們一直相愛相殺,
沒有他們我們看不到五彩斑斕的黑和能上天的程式碼。
那就是ui設計師和前端開發工程師們!
在這場戰爭中,程式猿小鍋鍋們髮際線日益上漲,設計師小姐姐們嗓子日愈暗啞。於是我們發起了一場拯救髮際線和保護金嗓子的行動。 讓機器看一眼,設計師的設計圖,然後自動完美還原設計圖,從根本上解決這場鬥爭的原因。從圖片到程式碼,即UI2CODE專案,詳細介紹可以檢視我們之前的文章。
經過我們不懈的努力終於能夠將圖片自動生成code,比較好的去還原設計師小姐姐的設計圖了,小姐姐的嗓子也日益甜美了。但是因為機器不懂程式碼的和圖片的真正意義,轉出來的程式碼可讀性比較差,看的令人頭大。
不僅如此,程式猿小鍋鍋們覺得既然解放自己一定要徹底,順便讓機器幫忙把資料繫結也做了吧。
UI語意解析
在圖片經過機器識別+ocr識別後(具體識別流程可檢視之前的文章),能夠將輸入圖識別成文字(text)、圖片(image)和形狀(shape)三個型別的基礎元素。然後透過佈局演算法產出一棵完整的佈局樹DT。
我們要做的事是解析出DT的葉子節點和枝節點各自的意義,而不是簡單的叫text,image。要解決這個問題,本質上就是理解出DT的葉子節點以及枝節點本身的意義的什麼。
我們的解法是:
(1)識別葉子節點語義
(2)透過葉子節點推測枝節點語義
具體解決方法如下:
我們目前使用百度nlp+alinlp組合的方式來對所有的text元素內容進行分析,之所以是組合的方式,因為目前百度的nlp對通用文字的型別解析更加全面並且接受自定義設定分詞類別的功能,但是alinlp在電商實體型別的識別方面更加出色。
整體的使用策略流程如下:
nlp給出的最終得到的文字解析主要資訊有詞性標註、專有名詞。
主要有如下類別(但是不僅限於這些類別,可自定義擴充):
這裡的影像是指從最初的輸入圖片中識別出來的image元素。這裡主要是對業務icon和需要被識別的圖片型別(如頭像)等小影像做了識別處理。
我們選用了YOLO模型來做識別。之所以選擇YOLO是因為它就是隻透過CNN網路,就能夠實現目標的定位和識別。同時yolo對小物體的識別精度較高,而icon和頭像等大多是小物件元素組成的。
但是yolo只能給出bounding box,所以在這個基礎上採用條件隨機場和馬爾可夫隨機場來對位置做進一步的位置校準。
只解析出葉子節點本身的語義還是不夠的,比如因為最終它在頁面上的意義還跟它的佈局位置和周圍其他的元素有關,於是我們同時需要分析出葉子節點的佈局意義。
以名稱(name)的推導為例,首先我們要定義出具體怎麼樣的文字為name,假定將描述圖片的和描述一段文字的的text文字稱為name。如下圖,紅框框的文字我們透過位置關係上的解析得到它name的屬性:
定義清楚name後我們就可以很容易用規則找到
(1)在圖片四周出現的文字很大機率是描述圖片以及當前區域相關的主要資訊,尤其是字型和顏色突出的文字;
(2)離一段描述文字的四周出現的,字型或者顏色更加突出的文字
最後將以上的文字在positional屬性上解析為name。
準確解析出name後也是不夠的,name具體是哪一類的名稱,是受到它附近的圖片或者段落影響的。比如(1)條件中圖片是icon,那麼就推斷為icon_name。
當經過以上幾個維度的解析後,我們就基本能獲得基礎元素的全面資訊了。但是這個元素到底是什麼,還需要有一個決策演算法去做決斷。為每個葉子節點設定一個Semantic類去記錄葉子節點解析出來的各個資訊。
Class Semantic(view){
# 預設的語義為元素的型別 如text/image/shape
this.default = view.type,
# nlp 解析出的結果
this.nlp = '',
# 影像識別出來的結果
this.recognition = '',
# 元素在頁面中的佈局位置解析結果
this.positional = ''
# 元素受上下文影響解析的結果
this.context = ''
# 文字元素翻譯結果
this.translation = ''
}
然後使用規則演算法將各類資訊加權。規則演算法即如果規則被X滿足,則稱該規則被觸發。並且列舉出所有的規則。
可能存在的問題:
(1)如果多個規則被觸發,但是它們指定了不同的類
(2)沒有一個規則被X滿足。
解決辦法:
(1)如果多個規則被觸發,則使用規模序和規則序策略解決。
規模序:把最高優先權賦予具有“最苛刻”要求的被觸發的規則。
規則序:預先確定規則的優先次序和權重,是基於類的或基於規則的
(2)沒有一個規則被X滿足的條件下,建立了一個省缺或預設規則。
首先我預先給葉子節點的五個屬性設定權重,比如default為0.05,文字詞性為0.1,translation為0.2,positional 為0.3,ci為0.5,nlp語義為0.4。然後根據規則條件覆蓋到的屬性簡單的將權重相加,並且每個屬性的使用複雜度為1。那麼每一條規則都有確定的權重值和複雜度。
關於解決辦法(1)的說明:
假設規則x為:解析結果為動詞的text那麼就輸出翻譯結果。這條規則的權重為0.3,複雜度為2;
規則R為:解析結果為動詞的text,ci識別屬性為button,positional屬性為name,那麼輸出為button_name。這條規則的權重為0.9,複雜度為3。
所以當一個元素同時滿足X和R兩個條件的時候,就使用在權重和複雜度上更大值的R為最終輸出。
關於解決辦法(2)的說明:
這個就比較簡單了,當元素沒有被任何一個規則滿足的時候就輸出元素的default屬性,即它自己本身的型別。
透過以上解析基本上的夠得到比較符合我們手寫程式碼的風格預期了:
為了方便看的更清楚,我在每個解析結果和對應的圖片的關鍵位置描了框和標記。可以看到,葉子節點的語義已經基本能夠比較好的識別解析出來了。
之後我們透過解析出來的葉子節點去推測枝節點語義。
"漫水式"演算法 推理枝節點語義
首先我們先定義需要被識別的枝節點,比如commodity。然後設定該組成commodity枝節點的葉子節點的型別的權重。
例如:組成commodity的葉子節點有
type | weight |
---|---|
price | 0.2 |
commodity_name | 0.5 |
commodity_pic | 0.3 |
從type為price的葉子節點出發,向它的四周擴散去尋找組成commodity枝節點的所有可能的葉子節點。直到有明確物理切割的邊緣或者新增葉子節點不再增加權重為止。
如果只考慮權重最大化原則的話,當遇到重複佈局的時候,會將多個相同的枝節點識別成一個大的枝節點。會出現以下情況:
預期選出藍色框的枝節點,實際得到的是紅色框的枝節點。所以我們還要設定剪枝演算法,將重複佈局等枝葉節點去掉。然後能大致得到比較符合預期的枝節點了。
但是最滿意的枝節點為綠色框的節點,於是需要設定一定的演算法規則去增加枝葉 。比如明確物理切割的邊緣等資訊。
至此語義解析上就能夠比較好的解析出元素和數結構的的真正語義內容了。
在資料自動繫結和提取上的應用
解析出各個層級和元素的內容後,就可以將資料進行對映和提取。例如:
這個ui,識別對應的語義後。我們對資料進行抽離產出viewmodel:
同時推進服務端給出統一的或者固定的的資料model,那麼就可以將識別出來的viewmodel和介面資料進行一一對應和自動對映繫結,如此可以省去許多之前前端需要和服務端聯調的工作。
至次,關於語義識別的流程算是完成了。再看一下生成的vue程式碼,感覺是不是已經能比較好的翻譯出可讀性比較友好的程式碼了。