开发者学堂课程【达摩院视觉AI课:图像识别项目讲解及使用说明 】学习笔记,与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/335/detail/3836
图像识别项目讲解及使用说明
内容介绍
一、基本介绍
二、Controller
三、Resource Service
四、Vision Service
五、前端页面
一、基本介绍
本节课制作一个电子相册。电子相册是通过阿里云视觉智能平台提供的算法进行图片识别,之后根据图片识别的结果对上传的图片进行分类。现在将项目运行起来。
可以看到现在项目运行在8080端口。看一下index.html就是主页,现在已经运行起来了。
由于现在没有图片,看到的只是三块空白,最上面的空白可以通过点击上传,也可以通过拖拽上传图片。
通过拖拽,可以看到下面的效果:
这是一张图片,可以看到本期图片分类,有两类视觉的算法:1、表情识别;2、场景识别。
上面的两个识别,现在再上传一张照片看一下效果。通过拖拽的方式上传图片。
可以点击下面的关键字选择图片。这样的话可以看到不同的效果。
再多上传几张图片,这里可以看到目前有惊讶的表情、生气的表情。以及露营户外、人物等,这样可以拆分图片。
以上是实现的功能。
回到代码中,现在讲解一下结构:
打开右侧导航栏中的Java,可以看到album application:
album application是启动的入口函数。整个结构:common存放一些公共的类或者一些常量还有一些值都放在common中;
Config需要做一些装载、一些数据库的配置在config中进行。
这个webappconfig做的是对静态资源,例如:css、js以及图片对其做了映射。
例如现在是static把它映射到classpath static这个目录下。如果在页面中写成这种格式的话,就会自动的在下图的目录中导入到对应的资源。
Controller:现在大部分的web采用了vc这种方式去提供服务,Controller就是接收web的请求,一般会做一些参数的校验,通过接口调用或将得到的数据返回到前端,这是Controller的作用。
Service:将不同的请求不同的服务抽样成一个Service。例如在这个项目中会有两个Service,就可以认为成有两个模块。
Resource:加载数据以及保存数据。由于为了简单起见,就不去做一些复杂的数据结构,比如持久化到数据库(之后可以自己实现这个功能)为了简单起见,可以快速学习就将其直接保存到本地文件中。
Vision:主要实现所有的算法,对图像识别都是通过Vision来实现的。例如函数recognize sence需要识别这个图片都符合哪些场景。
Recognize expression人脸图片包括哪些表情。
本节课主要以两个识别的场景作为讲解的主要内容。
Utils:放一些公共的类、一些工具函数
接下来深入了解上面介绍到的Service。主要的逻辑都放在了Service里面。
二、Controller
获取图片,将所有的图片都通过get on photos这个Service获取到所有的图片。还包括获取分类,通过分类以及标签获取图片,通过分类获取图片列表。另外就是上传文件并且在这个函数中会做几件事情。
1、上传。
将上传的图片保存到文件当中,并且要调用两个识别的函数。点击函数:下图就是表情识别和场景识别的部分。
注意:通过multipartfile获取的是文件的输入流,输入流只能读取一次,如果重复读的话就会是空,所以将它转化成bytearrayinput stream这个流,然后在每次用完之后,进行set,然后进行mark0,这时就可以重新使用input流。这时就会把流保存在内存当中。如果在线下,最好不要使用这种方式。
如果图片较大,会占用比较多的内存空间。为了简单实现,因此选择这种方式做。另外由于上传的图片可能会有重名,为了避免重名可以对图片的input流求一个md值作为函数名,这样基本可以避免文件名重复的问题。
三、Resource Service
可以将它理解为资源的管理器。
首先看一下postconstruct函数。在装载bing的时候会先去执行这个函数,在这个切入点可以将保存在本地的数据取出来加载到内存里面。在销毁对象的时候,再将数据保存在本地文件中。
可以看到保存在data下面的data.data数据。由于现在看到的还是空,需要在它停止之后再打开它就会执行函数,这时就会把数据完全的保存在data.json中。
结构:
上图是整个数据存储的结构。Allimg是所有上传的图片,都会放到这个数组里面。
分为两个场景:
还有不同的分类。可以看到有一个表情识别,还有一个场景识别。这个里面放的是所有识别出来的表情,例如:惊讶、生气、开心等内容。
场景会包括一些运动、户外。
做这么的存储就是为了能够快速的进行查询。可以通过分类快速的找到某一个分类下面的所有识别。可以看到场景这个分类下面所有的标签,或者是表情下面的所有标签。以上就是这个标签的作用。
Expression map下面有生气和惊讶两个标签,这个标签下面包括了哪些图片。
Scenemap和Expression map是同样的作用。
Imglable进行的是反向的查找过程,通过图片能够识别什么样的场景。
例如:可以识别出图片的人物场景、运动场景、生气表情、演出等场景。
这样做结构就可以通过不同的维度去查询。例如需要删除一个图片,这时可以很方便的找到,从哪删除图片。这样就不需要从整个大结构中删除图片。
现在没有实现删除图片的操作,下去可以制作一个删除的逻辑。
Addimge就是公共在讲解json的结构,例如删除一张图片,怎样去构造结构方便查询。
四、Vision Service
作用:不停的识别场景、表情。通过上传的图片流,将整个图片流传到接口里面,然后从服务端识别场景。
有两种方式:早期的版本是通过UR的方式上传的但是有一个限制,必须用oss.cn的对象才可以识别。所有后面新版本的SDK开始支持通过本地上传图片来进行识别。
五、前端页面
1、基本介绍
Idext.html就是刚刚看到的下图页面访问的:
前端页面是通过will加上element-ui组件实现前端的逻辑。
可以看到整个的结构分为三部分
class=" container">
<
div class="upload" id="upload">
class="upload-demo"
drag
action="http://127.0.0.1:8080/album/v1/upload"
:on-success="uploadSuccess"
Multiple
>
<
i
class-"el-icon-upload">
i>
<
div class="el-upload_text">将文件拖到此处,或
n>
点击上传
><
/div
>
i
p">只能上传jpg/png文件,且不超过
5
00kb
<
/div
>
>
这一部分是通过一个上传的组件来实现拖拽上传以及点选。
试一下点击上传,找到一个图片,点击打开。如果失败,重启项目再次重新操作。两种方式,一种拖拽一种点击上传。是通过一个组件实现的。组件实现有不同的功能。
action上传图片要去访问后面的接口,这个就是访问upload的接口:"http://127.0.0.1:8080/album/v1/upload"
下面是一个事件指的是成功之后要做那些事情。"uploadSuccess"就是后面执行要用的函数。例如上传成功之后要刷新页面、添加标签、分类等
第二部分就是看到的如下图部分:
这部分可以实现轮播图以及下面的列表。
2、vue-gallery组件
下面是一个vue的组件
<
div class="container" id="app">
>
>
下面也是一个实现自定义标签的组件v-tag就是图片下面的不同的分类,不同的分类下面有不同的标签。
<
v-tag:tags="tags">
>
<
script srce"js/script.js">
>
l>
Vue实现的组件都放在了vue.Component(vue-gallery)
组件vue-gallery就是实现自定义的标签名。可以在下图中看到:
可以在hml中用自定义的标签。这里会定义一个属性:photos。这个photos就是从后台取回来的数据,渲染到前端的页面当中。由于这是一个组件会有对应的模板,下图就是整个的模板部分:
在模板部分会对背景图片进行处理以及一些事件的定义。
回到idext.html中可以看到vue是整个事件的数据流,通过数据的不同变化可以去触发不同的渲染,例如上面上传的图片,是可以从对应不同的组件进行交互。例如通过上传图片,会显示先上传图片、不同的标签,这些都是通过事件来驱动不同的变化的。
回到script。Mounted里面就会定义了一些事件。例如图片变化、数据如何调用以及如何监听、按下不同的键应该做哪些操作。是前一张图片还是后一张图片。
这里面定义的是一些方法,这些方法在模板当中点击next photo,就会访问下一张图片。可以看到操作是相互对应的。
Vue会有自己的模板语言,可以看到定义方法,会生成一个vue的一个变量。可以认为这一块是视图的一部分。下面的可以认为是control控制器部分。data里面定义的数据就是在模板里面要用的数据。el是一个ID的绑定,例如#app,可以看到在这个标签的上层父级会定义ID,叫app。这时就可以将组件放在父的div下面,在里面去根据自己的模板去渲染。
let albumIns = new Vue({
el:'#app',
data() {
return {
}
}
created() {
this.getData()
).
mounted() (
customEvent.
$
Son(e
:
"uploadSuccess, n: success =
>{
this.getData()
://箭头西数内部不会产生新的this,这边如果不用=>,this指代Event
})
刷新页面之后就会请求接口getdata。点击进去,请求接口就是将所有的图片获取过来,然后放在photos属性里面。
methods: (
getData: async function() {
Let res = await axios. get ("htto://127.0.0.1:8080/album/v1/list")
This.photos=res.data
Console.log(resdata)
这时发现属性有值了,就会渲染模板,这就是通过数据渲染的流程。
class="container"
id
=
"app">
:photos="photos">
>
>
上面代码中的photos就是相当于将属性绑定到photos变量上面,这时就可以在下面代码里面用photos
template:
url('+photos [activePhoto]+'): "">
v-for=" (photo, index) in photos"
isrc="photo"
:key=" index"
@click=" changePhoto (index) "
:class="('active':activePhoto==index)":style="background-image: url('+photo+')"">
v-for=" (photo, index) in photos"
这就是一个vue的一个模板语言,for循环。会将里面的渠道的数据,例如索引位置、班级图片的地址把他们放在背景图片里面,然后完成整个渲染过程。以上就是组件大概的功能。
3、v-tag组件
这个组件在页面中:
上图就是v-tag渲染之后的结果。
接下来看一下v-tag组件中有什么内容。
Data就是在初始化的时候要进行哪些渲染?哪些数据。下面的代码就是自己定义的一些不同的颜色。使页面展示出来比较美观。
Catemap:由于后面返回的是英文,后面可以将其转化为汉字显示出来。
实现的方法:取一个不同的ID,然后建立出不同的颜色。页面中看到的不同的颜色就是通过取了随机二点的下标然后去渲染出不同的颜色。
”</span></code><code><span class="lake-fontsize-1515">><s/script></span></code></div><div><code><span class="lake-fontsize-1515"></body></span></code></div><div><code><span class="lake-fontsize-1515"></html></span></code><span class="lake-fontsize-14"></span></div><div><span class="lake-fontsize-1515">上面代码就是绑定上的数据。这个数据的结构是两重map结构,大概就是:每次结构为表情,下面有几种表情;场景下面有多种场景。这是两层map结构。</span><span class="lake-fontsize-14"></span></div><div><span class="lake-fontsize-1515">所以在渲染的时候需要对两层map进行渲染,渲染第一层,完成之后再渲染下面的第二层数据。</span><span class="lake-fontsize-14"></span></div><div><span class="lake-fontsize-1515"><strong><strong>4、不同的触发</strong></strong></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">上传图片成功之后会在页面中加一张图片,在tag里面加上不同的tag。这是如何实现的呢?</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">找到上传组件</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><code><span class="lake-fontsize-1515">let uplandins</span></code><code><span class="lake-fontsize-1515">=</span></code><code><span class="lake-fontsize-1515">new Vue(</span></code><code><span class="lake-fontsize-1515">{</span></code></div><div style="text-align: justify;"><code><span class="lake-fontsize-1515">el:’</span></code><code><span class="lake-fontsize-1515">#</span></code><code><span class="lake-fontsize-1515">up load'</span></code></div><div style="text-align: justify;"><code><span class="lake-fontsize-1515">methods:</span></code><code><span class="lake-fontsize-1515">{</span></code></div><div style="text-align: justify;"><code><span class="lake-fontsize-1515">uploadSucces</span></code><code><span class="lake-fontsize-1515">s</span></code><code><span class="lake-fontsize-1515">: function(esponse, f</span></code><code><span class="lake-fontsize-1515">i</span></code><code><span class="lake-fontsize-1515">le, fiLeList)</span></code><code><span class="lake-fontsize-1515">{</span></code></div><div style="text-align: justify;"><code><span class="lake-fontsize-1515">customEvent.$emit(e:‘uploadSuccess’</span></code><code><span class="lake-fontsize-1515">,</span></code><code><span class="lake-fontsize-1515">true):</span></code></div><div style="text-align: justify;"><code><span class="lake-fontsize-1515">customEvent. $emit(</span></code><code><span class="lake-fontsize-1515">e</span></code><code><span class="lake-fontsize-1515">:'refreshTag', true)</span></code><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">页面中onsuccess,成功之后在后面绑定了函数,成功之后会执行</span><span class="lake-fontsize-1515">uploadSuccess</span><span class="lake-fontsize-1515">函数。会找到这个函数,然后执行里面的公共逻辑。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">由于不同的组件之间需要通信,为了通信就可以定义虚拟的event的一个实例,通过它把所有的不同的vue实例串联起来。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">例如:通过</span><span class="lake-fontsize-1515">emit</span><span class="lake-fontsize-1515"> </span><span class="lake-fontsize-1515">uploadSuccess</span><span class="lake-fontsize-1515">去发送一个事件,用</span><span class="lake-fontsize-1515">refreshTag</span><span class="lake-fontsize-1515">发送一个事件,那么可以看到</span><span class="lake-fontsize-1515">refreshTag</span><span class="lake-fontsize-1515">这个是暂不支持实现的。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">以上就是通过</span><span class="lake-fontsize-1515">upload</span><span class="lake-fontsize-1515">实例去给其他的实例发事件。上传成功之后要把它加到图片的轮播图里面。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">Customevent.$on这个on就是对应的接收下面emit传过来的事件</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">现在是</span><span class="lake-fontsize-1515">uploadSuccess</span><span class="lake-fontsize-1515">事件。如果得到了</span><span class="lake-fontsize-1515">uploadSuccess</span><span class="lake-fontsize-1515">事件,这时候就通过后端去获取数据对页面进行刷新操作。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">第二可以看到</span><span class="lake-fontsize-1515">refresh</span><span class="lake-fontsize-1515">,同样的道理,上传成功之后要对整个的tag进行刷新,将新识别出来的不同的场景,不同的标签对其进行刷新。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">以上就是前端实现的大致逻辑。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515"> </span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">讲解了前端后端实现的逻辑,接下来可以在讲解之后可以做几件事情。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515"><span>1、</span></span><span class="lake-fontsize-1515">将本地存储这一部分逻辑替换成用数据库的方式存储。例如可以存到mysqly数据库,可以定义成不同的模型。这样更方便查询。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515"><span>2、</span></span><span class="lake-fontsize-1515">扩充更多的识别算法,例如这里实现了表情和场景。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F9a502c9e043846f8b3f7160f0290520a.png%22%2C%22originWidth%22%3A1738%2C%22originHeight%22%3A437%2C%22name%22%3A%2285.png%22%2C%22size%22%3A327356%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A1738%2C%22height%22%3A437%7D"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515">之后可以在文档当中实现不同的识别。甚至可以对图片实现美妆、美型、美颜等。还包括其他的文字识别等,都可以通过这种方式去上传。</span><span class="lake-fontsize-14"></span></div><div style="text-align: justify;"><span class="lake-fontsize-1515"> </span></div>