背景
在这个世界里,有一群人,
他们一直相爱相杀,
没有他们我们看不到五彩斑斓的黑和能上天的代码。
那就是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代码,感觉是不是已经能比较好的翻译出可读性比较友好的代码了。