UI2Code智能生成Flutter代码--整体设计篇

背景:
  随着移动互联网时代的到来,人类的科学技术突飞猛进。然而软件工程师们依旧需要花费大量精力在重复的还原UI视觉稿的工作。
UI视觉研发拥有明显的特征:组件,位置和布局,符合机器学习处理范畴。能否通过机器视觉和深度学习等手段自动生成UI界面代码,来解放重复劳动力,成为我们关注的方向。

UI2CODE简单介绍:
  UI2CODE项目是闲鱼技术团队研发的一款通过机器视觉理解+AI人工智能将UI视觉图片转化为端侧代码的工具。

  2018年3月UI2CODE开始启动技术可行性预研工作,到目前为止,经历了3次整体方案的重构(或者重写)。我们参考了大量的利用机器学习生成代码的方案,但都无法达到商用指标,UI2CODE的主要思想是将UI研发特征分而治之,避免鸡蛋放在一个篮子里。我们着重关注以下3个问题的解法:

视觉稿还原精度:我们的设计师甚至无法容忍1像素的位置偏差;
准确率:机器学习还处于概率学范畴,但我们需要100%的准确率;
易维护性:工程师们看的懂,改的动是起点,合理的布局结构才能保障界面流畅运行。
UI2CODE运行效果:
  UI2CODE插件化运行效果,如下视频:进过几轮重构,最终我们定义UI2CODE主要解决feeds流的卡片自动生成,当然它也可以对页面级自动生成。
视频连接:https://yunqivedio.alicdn.com/od/mm4Te1551157852340.mov

架构设计:

UI2Code智能生成Flutter代码--整体设计篇

简化分析下UI2CODE的流程:

UI2Code智能生成Flutter代码--整体设计篇

大体分为4个步骤:

1.通过机器视觉技术,从视觉稿提取GUI元素
2.通过深度学习技术,识别GUI元素类型
3.通过递归神经网络技术,生成DSL
4.通过语法树模板匹配,生成flutter代码
版面分析
  版面分析只做一件事:切图。

  图片切割效果直接决定UI2CODE输出结果的准确率。我们拿白色背景的简单UI来举例:

UI2Code智能生成Flutter代码--整体设计篇

上图是一个白色背景的UI,我们将图片读入内存,进行二值化处理:

def image_to_matrix(filename):
im = Image.open(filename)
width, height = im.size
im = im.convert("L")
matrix = np.asarray(im)
return matrix, width, height
得到一个二维矩阵:将白色背景的值转化为0.

UI2Code智能生成Flutter代码--整体设计篇

像切西瓜一样,我们只需要5刀,就可以将GUI元素分离,切隔方法多种多样:(下面是横切的代码片段,实际切割逻辑稍微复杂些,基本是递归过程)

def cut_by_col(cut_num, _im_mask):
zero_start = None
zero_end = None
end_range = len(_im_mask)
for x in range(0, end_range):
im = _im_mask[x]
if len(np.where(im==0)[0]) == len(im):
if zero_start == None:
zero_start = x
elif zero_start != None and zero_end == None:
zero_end = x
if zero_start != None and zero_end != None:
start = zero_start
if start > 0:
cut_num.append(start)
zero_start = None
zero_end = None
if x == end_range-1 and zero_start != None and zero_end == None and zero_start > 0:
zero_end = x
start = zero_start
if start > 0:
cut_num.append(start)
zero_start = None
zero_end = None
客户端的UI基本都是纵向流式布局,我们可以先横切在纵切。
demo3.png
将切割点的x,y轴坐标记录下来,它将是处理组件位置关系的核心。切割完成后,我们获取到2组数据:6个GUI元素图片和对应的坐标系记录。后续步骤通过分类神经网络进行组件识别。

在实际生产过程中,版面分析会复杂些,主要是在处理复杂背景方面。
demo5.png

关注我们的技术公众号,我们后续会详细分解。

组件识别:
  进行组件识别前我们需要收集一些组件样本进行训练,使用Tensorflow提供的CNN模型和SSD模型等进行增量训练。

  UI2CODE对GUI进行了几十种类型分类:IMAGE, TEXT,SHAP/BUTTON,ICON,PRICE等等,分别归类为UI组件,CI组件和BI组件。

UI组件,主要针对flutter原生的组件进行分类。
CI组件,主要针对闲鱼自定义UIKIT进行分类。
BI组件,主要针对具备一定业务意义的feed卡片进行分类。

demo4.png

组件的识别需要反复的通过全局特征反馈来纠正,通常会采用SSD+CNN协同工作,比如下图的红色“全新“shape,这该图例中是richtext的部分,同样的shape样式可能属于button或者icon。

图例1.png

属性提取:
  这块的技术点比较杂,归纳起来需要处理3部分内容:shape轮廓, 字体属性和组件的宽高。

demo6.png
完成属性提取,基本上我们完成所有GUI信息的提取。生成的GUI DSL如下图:

demo7.png
通过这些数据我们就可以进行布局分析了。
其中文字属性的提取最为复杂,后续我们会专门介绍。

布局分析:
  前期我们采用4层LSTM网络进行训练学习,由于样本量比较小,我们改为规则实现。规则实现也比较简单,我们在第一步切图时5刀切割的顺序就是row和col。缺点是布局比较死板,需要结合RNN进行前置反馈。

0:00
/ 0:40

视频中展示的是通过4层LSTM预测布局结构的效果,UI的布局结构就像房屋的框架,建造完成后通过GUI的属性进行精装修就完成了一个UI图层的代码还原工作。

代码生成及插件化:
  机器学习本质上来说还属于概率学范畴,自动生成的代码需要非常高的还原度和100%的准确率,概率注定UI2CODE很难达到100%的准确率,所以我们需要提供一个可编辑工具,由开发者通过工具能够快速理解UI的布局结构和修改布局结构。
  我们将UI2CODE生成的DSL TREE进行代码模板化匹配,代码模板的内容由资深的flutter技术专家来定义,它代表目前我们发现的最优代码实现方案。
001.png
代码模板中会引入一些标签,由Intellij plugin来检索flutter工程中是否存在对应的自定义UIKIT,并进行替换,提高代码的复用度.
demo11.png

整个插件化工程需要提供自定义UIKIT的检索,替换和校验工作,以及DSL Tree的创建,修改,图示等工作,总体来说,更像ERP系统,花费一些时间能够做的更加完美。
002.png

小结:
  本篇我们简单介绍了UI2CODE的设计思路,我们将整个工程结构分为5个部分,其中4块内容核心处理机器视觉的问题,通过机器学习将它们链接起来。代码的线上发布是非常严格的事情,而单纯的机器学习方式,很难达到我们要求,所以我们选择以机器视觉理解为主,机器学习为辅的方式,构建整个UI2CODE工程体系。我们将持续关注AI技术,来打造一个完美的UI2CODE工具。

原文地址:https://blog.51cto.com/14031893/2357945

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要讲解了“FlutterComponent动画的显和隐怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究...
这篇文章主要讲解了“flutter微信聊天输入框功能如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“f...
本篇内容介绍了“Flutter之Navigator的高级用法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处...
这篇文章主要介绍“Flutter怎么使用Android原生播放器”,在日常操作中,相信很多人在Flutter怎么使用Android原生播放器问题上存在疑惑,小编查阅了各式资料,整...
Flutter开发的android端如何修改APP名称,logo,版本号,具体的操作步骤:修改APP名称找到文件:android\\app\\src\\main\\AndroidManifest.xml
Flutter路由管理初识路由概念一.路由管理1.1.Route1.2.MaterialPageRoute1.3.Navigator1.4.路由传值1.5 命名路由1.6.命名路由参数传递1.7.适配二、路由钩子三、onUnknownRoute四、结尾初识路由概念路由的概念由来已久,包括网络路由、后端路由,到现在广为流行的前端路由。无论路由的概念如何应用,它的核心是一个路由映射表。比如:名字 detail 映射到 DetailPage 页面等。有了这个映射表之后,我们就可以方便的根据名字来完成路由的转发
前提:针对Android开发者(windows系统下),已安装Git,AndroidStudio(建议4.0+版本)一.下载Flutter SDK地址:https://flutter.dev/docs/development/tools/sdk/releases,在 Stable channel (Windows)里面下最新版本即可。Flutter的渠道版本会不停变动,请以Flutter官网为准。在中国,要想正常获取安装包列表或下载安装包,可能需要翻墙,也可以去Flutter github项目下去下载安
一、变量变量是一个引用,根据Dart中“万物皆对象”原则,即变量存储的都是对象的引用,或者说它们都是指向对象。1.1.声明变量://1.不指定类型var name = 'aaa';//2.明确指定类型String name = 'aaa';因为有类型推导,所以两种实现效果一样,官方推荐在函数内的本地变量尽量使用var声明。在变量类型并不明确的情况下,可以使用dynamic关键字//3.使用dynamic关键字dynamic name = 'aaa';1.2.默认值未初始化的变量
前言Flutter2.0发布不久,对web的支持刚刚进入stable阶段。初学几天,构建web应用时候碰到一些问题,比如中文显示成乱码,然后加载图片出现图片跨域问题:Failed to load network image...Trying to load an image from another domain?1.开启web端构建:使用下面这个命令才可以开启Web端构建的支持flutter config --enable-web提示我们:重新启动编辑器,以便它们读取新设置。2.重
一.Flutter打Android release包的步骤:1.为项目创建一个.jks签名文件(很简单,跳过)2.创建一个文件key.properties,直接复制下面key.properties位置如图:在里面输入一下内容:storePassword=iflytekkeyPassword=iflytekkeyAlias=teachingmachinestoreFile=E:/teacher/app/keys/TeachingMachine.jks输入你自己的passwork以及
1 问题Android原生向js发消息,并且可以携带数据2 实现原理Android原生可以使用RCTEventEmitter来注册事件,然后这里需要指定事件的名字,然后在js那端进行监听同样事件的名字监听,就可以收到消息得到数据Android注册关键代码reactContext.getJSModule(DeviceEventManagerModule.RCT...
1 Flexbox布局1) flexDirection 可以决定布局的主轴,子元素是应该沿着水平轴(row)方向排列,还是沿着竖直轴(column)方向排列2) justifyContent 决定其子元素沿着次轴(与主轴垂直的轴,比如若主轴方向为row,则次轴方向为column)的排列方式 有flex-start、center、flex-end、space-around...
1 实现的功能在网上看React Native文档,我特码就想实现一个页面到另外一个页面的跳转,然后另外一个页面怎么获取参数,特么没找到一个说清楚的,要么太复杂,要么说了不理解,下面是我自己写的一个App.js文件,实现一个Home页面跳到另外Details页面,并且携带了参数怎么在Details页面获取,就是这么简单粗暴.2 测试DemoApp.js文件如下...
1 问题在一个文件构建一个对象,然后在另外一个文件里面new这个对象,通过构造方法传递参数,然后再获取这个参数2 测试代码Student.js文件如下'use strict';import React from 'react'import {NativeModules, NativeEventEmitter, DeviceEventEmitter,Ale...
1 简单部分代码export default class App extends Component<Props> { render() { return ( <View {styles.container}> <View {styles.welcome}> &l...
1 怎么实现发送和接收事件理论上封装了Android原生广播的代码,需要注册和反注册,这里用DeviceEventEmitter实现//增加监听DeviceEventEmitter.addListener//取消监听//this.emitter.remove();这里可也可以通过安卓原生向页面js发送消息,可以参考我的这篇博客React Native之Android原生通过Dev...
1、Component介绍一般Component需要被其它类进行继承,Component和Android一样,也有生命周期英文图片如下2 具体说明1)、挂载阶段constructor()//构造函数,声明之前先调用super(props)componentWillMount()//因为它发生在render()方法前,因此在该方法内同步设置状态...
1 触摸事件普通点击我们可以使用onPress方法,我们可以使用Touchable 系列控件设计我们的按钮TouchableHighlight 背景会在用户手指按下时变暗TouchableNativeFeedback用户手指按下时形成类似墨水涟漪的视觉效果TouchableOpacity指按下时降低按钮的透明度,而不会改变背景的颜色TouchableWithoutFeedbac...
1 问题部分代码如下class HomeScreen extends React.Component { render() { return ( <View {{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Scre...
1 Props(属性)和State(状态)和简单样式简单使用App.js代码如下/** * Sample React Native App * https://github.com/facebook/react-native * * @format * @flow */import React, {Component} from 'react';import {Pla...