基于函数服务的图片识别应用
引导式阅读
Others
基于函数服务的图片识别应用
作者
HDC.Cloud
上架时间
2021-07-05 11:26:37

1. 介绍

本指南将介绍一种新的设计模式:基于事件的实时数据处理。为了更形象地描述,我们以图片分类为例,先介绍通过APIG触发器如何构建一个图片分类的Web应用,再介绍通过OBS触发器如何构造一个实时的图片分类系统。 这里再次强调下无服务器架构相比于传统的架构,具有如下优点:

  • 无需关注任何服务器,只需关注核心业务逻辑,提高开发和运维效率
  • 事件触发,灵活扩展
  • 函数运行随业务量弹性伸缩,按需付费,执行才计费,对于负载波峰波谷非常明显的场景可以减少大量成本
  • 通过简单的配置即可连通函数工作流和其它各云服务,甚至云服务和云服务

我们可以通过函数工作流服务来快速构建这个系统,并且完全无需关注服务器,且弹性伸缩运行、按需计费,如图:

image

2. 准备工作

创建华为云账号并完成实名。

申请开通华为云图片检测服务的图片标签功能,开通后便可以调用图片标签接口了。

需要为当前函数设置委托,您需要将委托设置具有访问IAM的权限。

  • 委托类型:云服务
  • 云服务:函数工作流 FunctionGraph
  • 权限选择:FunctionGraph Administrator

3. 创建函数,使用空白模版

  • 函数名称: {任意字母或数字}
  • 委托名称: {准备工作中创建的委托}
  • 函数执行入口: index.handler
  • 运行时语言: Node.js 8.10
  • 代码在线编辑输入下面代码,之后创建函数

图片分类系统业务代码 Image-Recognition.txt

/** * This function is used to build a web application backend system for tagging images. * The function invokes the image tagging interface of the Image Recognition service to tag images, and provides an HTTP(S) API through an APIG trigger. * The body structure of a POST message for requesting the API is as follows: * { * image: 'Base64 code of an image' * } * The response body is as follows: * {"tags":["Park","Green","Human","Happy","Casual","Trees","Summer","Outdoor","Smile"]} * * Procedure: * 1. Create a function and an APIG trigger. Configure the None security authentication mode for the trigger in the test phase. * 2. After creating the function, specify an agency with IAM access permissions on the Configuration tab page, and apply for the Image Tagging function on the Image Recognition console. * 3. Use a tool such as Postman to call the URL of the APIG trigger by sending a POST request. Specify the Base64 code of an image in the request body. * * You can combine this template with the image-tag-app-web template to build a complete serverless image tagging web application. */ const https = require('https'); let CONTEXT = null; exports.handler = async (event, context) => { //Cross-origin verification if (event.httpMethod === "OPTIONS") { console.log('OPTIONS request, pass.'); const result = constructResponse('', 200); return result; } CONTEXT = context; const response = { tags: [] }; let resError = ''; const tagNames = await tagImage(event).catch((error) => { console.log(error); resError = error; }); if (tagNames) { response.tags = tagNames; return constructResponse(response, 200); } else { return constructResponse(resError, 500); } } function tagImage(event) { return new Promise((resolve, reject) => { const payload = constructPayload(event); const token = CONTEXT.getToken(); if (!token) { reject('Can\'t get token, please set agency with IAM '); } const options = { hostname: 'image.cn-north-1.myhuaweicloud.com', port: 443, path: '/v1.0/image/tagging', method: 'POST', headers: { 'Content-Type': 'application/json;charset=utf8', 'X-Auth-Token': token } }; const req = https.request(options, (res) => { const status = res.statusCode; console.log("tag request status:", status); res.on('data', (data) => { const dataObject = JSON.parse(data.toString()); console.log('tag result:', dataObject); if (status === 200) { const tags = dataObject.result.tags || []; const tagNames = tags.map(item => item.tag.replace(' ', '')); resolve(tagNames); } else { reject(dataObject); } }); }); req.on('error', (e) => { reject(e); }); req.write(JSON.stringify(payload)); req.end(); }); } function constructResponse(data = '', code = 200) { return { body: typeof data === 'object' ? JSON.stringify(data) : (data + ''), headers: { "Content-Type":"application/json", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "Content-Type,Accept", "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE" }, statusCode: code, isBase64Encoded: false }; } function constructPayload(event) { if (event.httpMethod != "POST") { return { "image": "", "url": "https://bjbucket.obs.myhwclouds.com/70923411.jpg" } } const bodyStr = new Buffer(event.body, 'base64').toString('utf8'); let body = {}; try { body = JSON.parse(bodyStr); } catch(ex) { } const payload = { image: '', url: '', language: 'zh', limit: '10', threshold: CONTEXT.getUserData('THRESHOLD') || 50 } if (body.image && body.image.trim && body.image.trim()) { payload.image = body.image.trim(); } else { payload.url = body.url || ''; } return payload; }

4. 在函数中创建触发器

  • 触发器类型: API Gateway服务(APIG)
  • 安全认证: NONE
  • 请求协议: HTTPS
  • 后端超时 (毫秒): 5000

创建后获得调用URL:https://d7b17f433f48410381181bb1d678a833.apigw.cn-north-1.huaweicloud.com/picid

5. 创建前端页面函数。

  • 函数名称: {任意字母或数字}
  • 委托名称: {准备工作中创建的委托}
  • 函数执行入口: index.handler
  • 运行时语言: Node.js 6.10
  • 代码在线编辑输入下面代码,之后创建函数

图片分类系统业务代码 Image-Recognition.txt

/** * This function is used to build a web application frontend system for tagging images. * With the access URL of an APIG trigger, the function redirects requests to a deployed web page. * The environment variable REST_API specifies the address of the image tagging API provided by the backend system. * * You can combine this template with the image-tag-app template to build a complete serverless image tagging web application. */ const _ = require('lodash'); exports.handler = function(event, context, callback) { // Access Web const proto = event.headers['x-forwarded-proto'] || 'http'; const restApi = context.getUserData('REST_API'); if (!restApi) { console.log('ERROR: please set REST_API for the function.'); const result = constructResponse('please set REST_API for your web.', {}, 400); callback(null, result); return; } const webUrl = `${proto}://fgs-image-tag-app.obs-website.cn-north-1.myhwclouds.com/?rest-api-tag=${restApi}#/image-tag`; const result = constructResponse('', { Location: webUrl }, 302); console.log(`Access web, and rest api is ${restApi}, redirect to ${webUrl}`); callback(null, result); } function constructResponse(data = '', headers = {}, code = 200) { return { body: typeof data === 'object' ? JSON.stringify(data) : (data + ''), headers: _.merge({ "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "Content-Type,Accept", "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE" }, headers), statusCode: code, isBase64Encoded: false }; }

6. 在函数中添加环境变量

  • 键: REST_API
  • 值: https://d7b17f433f48410381181bb1d678a833.apigw.cn-north-1.huaweicloud.com/picid
  • 点击保存

7. 在函数中创建触发器

  • 触发器类型: API Gateway服务(APIG)
  • 安全认证: NONE
  • 请求协议: HTTPS
  • 后端超时 (毫秒): 5000

8.(可选): 通过在OBS上传代码包构建前端Web

  1. 下载前端代码包,并解压。(fgs-image-tag-app.zip)
  2. 修改代码包中的文件/functiongraph/assets/config/apis.json中checkImage的值,更改为函数(步骤1中创建的后端函数)的APIG触发器的调用URL,这样前端页面就会发请求去触发您的函数执行了。
  3. 上传代码包中的文件到OBS桶中,并将该桶访问权限设置为公共读。因为上传的文件较多,建议您使用OBS Browser批量上传。
  4. 配置该桶为静态服务器,并设置入口文件为index.html 上传完成后直接在OBS服务中获取index.html访问地址并打开,此方法与步骤3-4-5中创建的函数前端作用相同。

9. 恭喜你

你已经开发完了一个实时的图片分类系统。 访问前端:https://d7b17f433f48410381181bb1d678a833.apigw.cn-north-1.huaweicloud.com/picidweb

你已经成功完成了Codelab并学到了:

  • 一个简单无服务函数
  • 如何利用图片标签智能服务快速开发识别应用的原理。