如何用 Serverless 優雅地實現圖片藝術化應用

2021-02-20 TencentServerless

本文將分享如何從零開始搭建一個基於騰訊雲 Serverless 的圖片藝術化應用!

項目已開源,完整代碼見文末

線上 demo 預覽: https://art.x96.xyz/

在完整閱讀文章後,讀者應該能夠實現並部署一個相同的應用,這也是本篇文章的目標。

項目看點概覽:

前端 react(Next.js)、後端 node(koa2)全面使用 ts 進行開發,極致開發體驗(後端運行時 ts 的方案,雖然性能差點,不過勝在無需編譯,適合寫 demo)TensorFlow2 + Serverless 擴展想像力邊際高性能,輕鬆應對萬級高並發,實現高可用(自信的表情,反正是平臺幹的活)

本項目部署藉助了 Serverless component,因此當前開發環境需先全局安裝 Serverless 命令行工具

npm install -g serverless

需求與架構

本應用的整體需求很簡單:圖片上傳與展示。

模塊概覽上傳圖片瀏覽圖片用對象存儲提供存儲服務

在開發之前,我們先創建一個 oss 用於提供圖片存儲(可以使用你已有的對象存儲)

mkdir oss

在新建的 oss 目錄下添加 serverless.yml

component: cos
name: xart-oss
app: xart
stage: dev

inputs:
src:
src: ./
exclude:
- .env # 防止密鑰被上傳
bucket: ${name} # 存儲桶名稱,如若不添加 AppId 後綴,則系統會自動添加,後綴為大寫(xart-oss-<你的appid>)
website: false
targetDir: /
protocol: https
region: ap-guangzhou # 配置區域,儘量配置在和服務同區域內,速度更快
acl:
permissions: public-read # 讀寫配置為,私有寫,共有讀

執行 sls deploy 幾秒後,你應該就能看到如下提示,表示新建對象存儲成功。

新建對象存儲

這裡,我們看到 url https://art-oss-.cos.ap-guangzhou.myqcloud.com,可以發現默認的命名規則是 https://<名字-appid>.cos.<地域>.myqcloud.com

簡單記錄一下,在後面服務中會用到,忘記了也不要緊,看看 .env 內 TENCENT_APP_ID 欄位(部署後會自動生成 .env)

實現後端服務

新建一個目錄並初始化

mkdir art-api && cd art-api && npm init

安裝依賴(期望獲取 ts 類型提示,請自行安裝 @types)

npm i koa @koa/router @koa/cors koa-body typescript ts-node cos-nodejs-sdk-v5 axios dotenv

配置 tsconfig.json

{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"lib": ["es2018", "esnext.asynciterable"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true
}
}

入口文件 sls.js

require("ts-node").register({ transpileOnly: true }); // 載入 ts 運行時環境,配置忽略類型錯誤
module.exports = require("./app.ts"); // 直接引入業務邏輯,下面我會和你一起實現

補充兩個實用知識點:

node -r

在入口文件中引入 require("ts-node").register({ transpileOnly: true }) 實際等同於 node -r ts-node/register/transpile-only

所以 node -r 就是在執行之前載入一些特定模塊,利用這個能力,能快速實現對一些功能的支持

比如 node -r esm main.js 通過 esm 模塊就能在無需 babel、webpack 的情況下快速 import 與 export 進行模塊加載與導出

ts 加載路徑

如果不希望用 ../../../../../ 來加載模塊,那麼

在 tsconfig.json 中配置 baseUrl: "."ts-node -r tsconfig-paths/register main.ts 或 require("tsconfig-paths").register() import utils from 'src/utils' 即可愉快地從項目根路徑加載模塊

下面來實現具體邏輯:

app.ts

require("dotenv").config(); // 載入 .env 環境變量,可以將一些密鑰配置在環境變量中,並通過 .gitignore 阻止提交
import Koa from "koa";
import Router from "@koa/router";
import koaBody from "koa-body";
import cors from '@koa/cors'
import util from 'util'
import COS from 'cos-nodejs-sdk-v5'
import axios from 'axios'

const app = new Koa();
const router = new Router();

var cos = new COS({
SecretId: process.env.SecretId // 你的id,
SecretKey: process.env.SecretKey // 你的key,
});

const cosInfo = {
Bucket: "xart-oss-<你的appid>", // 部署oss後獲取
Region: "ap-guangzhou",
}

const putObjectSync = util.promisify(cos.putObject.bind(cos));
const getBucketSync = util.promisify(cos.getBucket.bind(cos));

router.get("/hello", async (ctx) => {
ctx.body = 'hello world!'
})

router.get("/api/images", async (ctx) => {
const files = await getBucketSync({
...cosInfo,
Prefix: "result",
});

const cosURL = `https://${cosInfo.Bucket}.cos.${cosInfo.Region}.myqcloud.com`;
ctx.body = files.Contents.map((it) => {
const [timestamp, size] = it.Key.split(".jpg")[0].split("__");
const [width, height] = size.split("_");
return {
url: `${cosURL}/${it.Key}`,
width,
height,
timestamp: Number(timestamp),
name: it.Key,
};
})
.filter(Boolean)
.sort((a, b) => b.timestamp - a.timestamp);
});

router.post("/api/images/upload", async (ctx) => {
const { imgBase64, style } = JSON.parse(ctx.request.body)
const buf = Buffer.from(imgBase64.replace(/^data:image\/\w+;base64,/, ""), 'base64')
// 調用預先提供tensorflow服務加工圖片,後面替換成你自己的服務
const { data } = await axios.post('https://service-edtflvxk-1254074572.gz.apigw.tencentcs.com/release/', {
imgBase64: buf.toString('base64'),
style
})
if (data.success) {
const afterImg = await putObjectSync({
...cosInfo,
Key: `result/${Date.now()}__400_200.jpg`,
Body: Buffer.from(data.data, 'base64'),
});
ctx.body = {
success: true,
data: 'https://' + afterImg.Location
}
}
});

app.use(cors());
app.use(koaBody({
formLimit: "10mb",
jsonLimit: '10mb',
textLimit: "10mb"
}));
app.use(router.routes()).use(router.allowedMethods());

const port = 8080;
app.listen(port, () => {
console.log("listen in http://localhost:%s", port);
});

module.exports = app;

在代碼裡可以看到,在圖片上傳採用了 base64 的形式。這裡需要注意,通過 api 網關觸發 scf 的時候,網關無法透傳 binary,具體上傳規則可以參閱官方文檔:

再補充一個知識點:實際我們訪問的是 api 網關,然後觸發雲函數,來獲得請求返回結果,所以 debug 時需要關注全鏈路

回歸正題,接著配置環境變量 .env

NODE_ENV=development

# 配置 oss 上傳所需密鑰,需要自行配置,配好了也別告訴我:)
# 密鑰查看地址:https://console.cloud.tencent.com/cam/capi
SecretId=xxxx
SecretKey=xxxx

以上,server 部分就開發完成了,我們可以通過在本地執行 node sls.js 來驗證一下,應該可以看到服務啟動的提示了。

listen in http://localhost:8080

來簡單配置一下 serverless.yml,把服務部署到線上,後面再進一步使用 layer 進行優化

component: koa # 這裡填寫對應的 component
app: art
name: art-api
stage: dev

inputs:
src:
src: ./
exclude:
- .env
functionName: ${name}
region: ap-guangzhou
runtime: Nodejs10.15
functionConf:
timeout: 60 # 超時時間配置的稍微久一點
environment:
variables: # 配置環境變量,同時也可以直接在scf控制臺配置
NODE_ENV: production
apigatewayConf:
enableCORS: true
protocols:
- https
- http
environment: release

之後執行部署命令 sls deploy

等待數十秒,應該會得到如下的輸出結果(如果是第一次執行,需要平臺方授權)

其中 url 就是當前服務部署在線上的地址,我們可以試著訪問一下看看,是否看到了預設的 hello world。

到這裡,server 基本上已經部署完成了。如果代碼有改動,那就修改後再次執行 sls deploy。官方為代碼小於 10M 的項目提供了在線編輯的能力。

但是,隨著項目複雜度的增加,deploy 上傳會變慢。所以,讓我們再優化一下。

新建 layer 目錄

mkdir layer

在 layer 目錄下添加 serverless.yml

component: layer
app: art
name: art-api-layer
stage: dev

inputs:
region: ap-guangzhou
name: ${name}
src: ../node_modules # 將 node_modules 打包上傳
runtimes:
- Nodejs10.15 # 注意配置為相同環境

回到項目根目錄,調整一下根目錄的 serverless.yml

component: koa # 這裡填寫對應的 component
app: art
name: art-api
stage: dev

inputs:
src:
src: ./
exclude:
- .env
- node_modules/** # deploy 時排除 node_modules
functionName: ${name}
region: ap-guangzhou
runtime: Nodejs10.15
functionConf:
timeout: 60 # 超時時間配置的稍微久一點
environment:
variables: # 配置環境變量,同時也可以直接在 scf 控制臺配置
NODE_ENV: production
apigatewayConf:
enableCORS: true
protocols:
- https
- http
environment: release
layers:
- name: ${output:${stage}:${app}:${name}-layer.name} # 配置對應的 layer
version: ${output:${stage}:${app}:${name}-layer.version} # 配置對應的 layer 版本

接著執行命令 sls deploy --target=./layer 部署 layer,然後這次部署看看速度應該已經在 10s 左右了

sls deploy

關於 layer 和雲函數,補充兩個知識點:

layer 的加載與訪問

layer 會在函數運行時,將內容解壓到 /opt 目錄下,如果存在多個 layer,那麼會按時間循序進行解壓。如果需要訪問 layer 內的文件,可以直接通過 /opt/xxx 訪問。如果是訪問 node_module 則可以直接 import,因為 scf 的 NODE_PATH 環境變量默認已包含 /opt/node_modules 路徑。

配額

雲函數 scf 針對每個用戶帳號,均有一定的配額限制:

其中需要重點關注的就是單個函數代碼體積 500mb 的上限。在實際操作中,雲函數雖然提供了 500mb。但也存在著一個 deploy 解壓上限。

關於繞過配額問題:

如果超的不多,那麼使用 npm install --production 就能解決問題如果超的太多,那就通過掛載 cfs 文件系統來進行規避,我會在下面部署 tensorflow 算法模型服務章節裡面,展開聊聊如何把 800mb tensorflow 的包 + 模型部署到 SCF 上實現前端 SSR 服務

下面將使用 next.js 來構建一個前端 SSR 服務。

新建目錄並初始化項目:

mkdir art-front && cd art-front && npm init

安裝依賴:

npm install next react react-dom typescript @types/node swr antd @ant-design/icons dayjs

增加 ts 支持(next.js 跑起來會自動配置):

touch tsconfig.json

打開 package.json 文件並添加 scripts 配置段:

"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}

編寫前端業務邏輯(文中僅展示主要邏輯,源碼在 GitHub 獲取)

pages/_app.tsx

import React from "react";
import "antd/dist/antd.css";
import { SWRConfig } from "swr";

export default function MyApp({ Component, pageProps }) {
return (
<SWRConfig
value={{
refreshInterval: 2000,
fetcher: (...args) => fetch(args[0], args[1]).then((res) => res.json()),
}}
>
<Component {...pageProps} />
</SWRConfig>
);
}

pages/index.tsx  完整代碼

import React from "react";
import { Card, Upload, message, Radio, Spin, Divider } from "antd";
import { InboxOutlined } from "@ant-design/icons";
import dayjs from "dayjs";
import useSWR from "swr";

let origin = 'http://localhost:8080'
if (process.env.NODE_ENV === 'production') {
// 使用你自己的部署的art-api服務地址
origin = 'https://service-5yyo7qco-1254074572.gz.apigw.tencentcs.com/release'
}

// 略...
export default function Index() {
const { data } = useSWR(`${origin}/api/images`);

const [img, setImg] = React.useState("");
const [loading, setLoading] = React.useState(false);

const uploadImg = React.useCallback((file, style) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = async () => {
const res = await fetch(
`${origin}/api/images/upload`, {
method: 'POST',
body: JSON.stringify({
imgBase64: reader.result,
style
}),
mode: 'cors'
}
).then((res) => res.json());

if (res.success) {
setImg(res.data);
} else {
message.error(res.message);
}
setLoading(false);
}
}, []);

const [artStyle, setStyle] = React.useState(STYLE_MODE.cube);

return (
<Dragger
style={{ padding: 24 }}
{...{
name: "art_img",
showUploadList: false,
action: `${origin}/api/upload`,
onChange: (info) => {
const { status } = info.file;
if (status !== "uploading") {
console.log(info.file, info.fileList);
}
if (status === "done") {
setImg(info.file.response);
message.success(`${info.file.name} 上傳成功`);
setLoading(false);
} else if (status === "error") {
message.error(`${info.file.name} 上傳失敗`);
setLoading(false);
}
},
beforeUpload: (file) => {
if (
!["image/png", "image/jpg", "image/jpeg"].includes(file.type)
) {
message.error("圖片格式必須是 png、jpg、jpeg");
return false;
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error("文件大小超過10M");
return false;
}
setLoading(true);

uploadImg(file, artStyle);
return false;
},
}}
// 略...

使用 npm run dev 把前端跑起來看看,看到以下提示就是成功了

ready - started server on http://localhost:3000

接著配置 serverless.yml(如果有需要可以參考前文,使用 layer 優化部署體驗)

component: nextjs
app: art
name: art-front
stage: dev

inputs:
src:
dist: ./
hook: npm run build
exclude:
- .env
region: ap-guangzhou
functionName: ${name}
runtime: Nodejs12.16
staticConf:
cosConf:
bucket: art-front # 將前端靜態資源部署到oss,減少scf的調用頻次
apigatewayConf:
enableCORS: true
protocols:
- https
- http
environment: release
# customDomains: # 如果需要,可以自己配置自定義域名
# - domain: xxxxx
# certificateId: xxxxx # 證書 ID
# # 這裡將 API 網關的 release 環境映射到根路徑
# isDefaultMapping: false
# pathMappingSet:
# - path: /
# environment: release
# protocols:
# - https
functionConf:
timeout: 60
memorySize: 128
environment:
variables:
apiUrl: ${output:${stage}:${app}:art-api.apigw.url} # 此處可以將api通過環境變量注入

由於我們額外配置了 oss,所以需要額外配置一下 next.config.js

const isProd = process.env.NODE_ENV === "production";

const STATIC_URL =
"https://art-front-<你的appid>.cos.ap-guangzhou.myqcloud.com/";

module.exports = {
assetPrefix: isProd ? STATIC_URL : "",
};

提供 Tensorflow 2.x 算法模型服務

在上面的例子中,我們使用的 Tensorflow,暫時還是調用我預先提供的接口。

接著讓我們會把它替換成我們自己的服務。

基礎信息

scf 在 python 環境下,默認提供了 tensorflow1.9 依賴包,使用 python 可以用較低的成本直接上手。

問題所在

但如果你想使用 2.x 版本,或不熟悉 python,想用 node 來跑 tensorflow,那麼就會遇到代碼包大小的限制的問題。

Python 中 Tensorflow 2.3 包體積 800mb 左右node 中 tfjs-node2.3 安裝後,同樣會超過 400mb(tfjs core 版本,非常小,不過速度太慢)

怎麼解決 —— 文件存儲服務!

先看看 CFS 文檔的介紹

掛載後,就可以正常使用了,騰訊雲提供了一個簡單例子。

var fs = requiret('fs');
exports.main_handler = async (event, context) => {
await fs.promises.writeFile('/mnt/myfolder/filel.txt', JSON.stringify(event));
return event;
};

既然能正常讀寫,那麼就能夠正常的載入 npm 包,可以看到我直接加載了 /mnt 目錄下的包,同時 model 也放在 /mnt 下

  tf = require("/mnt/nodelib/node_modules/@tensorflow/tfjs-node");
jpeg = require("/mnt/nodelib/node_modules/jpeg-js");
images = require("/mnt/nodelib/node_modules/images");
loadModel = async () => tf.node.loadSavedModel("/mnt/model");

如果你使用 Python,那麼可能會遇到一個問題,那就是 scf 默認環境下提供了 tensorflow 1.9 的依賴包,所以需要使用 insert,提高 /mnt 目錄下包的優先級

sys.path.insert(0, "./mnt/xxx")

上面提供了解決方案,那麼具體開發中可能會感覺很麻煩,因為 csf 必須和 scf 配置在同一個子網內,無法掛載到本地進行操作。

所以,在實際部署過程中,可以在對應網絡下,購置一臺按需計費的 ecs 雲伺服器實例。然後將硬碟掛載後,直接進行操作,最後在雲函數成功部署後,銷毀實例:)

sudo yum install nfs-utils
mkdir <待掛載目標目錄>
sudo mount -t nfs -o vers=4.0,noresvport <掛載點IP>:/ <待掛載目錄>

具體業務代碼如下:

const fs = require("fs");
let tf, jpeg, loadModel, images;

if (process.env.NODE_ENV !== "production") {
tf = require("@tensorflow/tfjs-node");
jpeg = require("jpeg-js");
images = require("images");
loadModel = async () => tf.node.loadSavedModel("./model");
} else {
tf = require("/mnt/nodelib/node_modules/@tensorflow/tfjs-node");
jpeg = require("/mnt/nodelib/node_modules/jpeg-js");
images = require("/mnt/nodelib/node_modules/images");
loadModel = async () => tf.node.loadSavedModel("/mnt/model");
}

exports.main_handler = async (event) => {
const { imgBase64, style } = JSON.parse(event.body)
if (!imgBase64 || !style) {
return { success: false, message: "需要提供完整的參數imgBase64、style" };
}
time = Date.now();
console.log("解析圖片--");
const styleImg = tf.node.decodeJpeg(fs.readFileSync(`./imgs/style_${style}.jpeg`));
const contentImg = tf.node.decodeJpeg(
images(Buffer.from(imgBase64, 'base64')).size(400).encode("jpg", { operation: 50 }) // 壓縮圖片尺寸
);
const a = styleImg.toFloat().div(tf.scalar(255)).expandDims();
const b = contentImg.toFloat().div(tf.scalar(255)).expandDims();
console.log("--解析圖片 %s ms", Date.now() - time);


time = Date.now();
console.log("載入模型--");
const model = await loadModel();
console.log("--載入模型 %s ms", Date.now() - time);


time = Date.now();
console.log("執行模型--");
const stylized = tf.tidy(() => {
const x = model.predict([b, a])[0];
return x.squeeze();
});
console.log("--執行模型 %s ms", Date.now() - time);

time = Date.now();

const imgData = await tf.browser.toPixels(stylized);
var rawImageData = {
data: Buffer.from(imgData),
width: stylized.shape[1],
height: stylized.shape[0],
};

const result = images(jpeg.encode(rawImageData, 50).data)
.draw(
images("./imgs/logo.png"),
Math.random() * rawImageData.width * 0.9,
Math.random() * rawImageData.height * 0.9
)
.encode("jpg", { operation: 50 });

return { success: true, data: result.toString('base64') };
};

最後

感謝閱讀,以上代碼均經過實測,如果發現異常,那就再看一遍:)

有其他問題或想法,歡迎評論區留言討論!

源碼:
github.com/jiangqizheng/art,歡迎 star。

原文:
https://zhuanlan.zhihu.com/p/218803108

One More Thing

立即體驗騰訊雲 Serverless Demo,獲取 Serverless 新用戶禮包,請在 PC 端訪問:

serverless.cloud.tencent.com/start?c=wx

相關焦點

  • 代碼零改動Serverless架構升級?這家在線編程教育企業這麼做的!
    腳踏實地先把技術發展起來"是風變科技CTO一直以來對團隊的要求,「用技術推動下一代的基礎教育」是風變的使命,也是每位風變人心中的理想。風變的開發模式、工具、腳手架已經標準化、流程化,存量業務正在線上穩定運行,如何將 Serverless 融入到現有開發模式和工具中,存量業務的遷移如何絲般潤滑等等技術痛點,在風變前端基建團隊的努力以及阿里雲Serverless雲開發平臺團隊的助力下,已經完成了serverless架構的整體建設,與前端工程化和自動化相結合,並且集成本地CICD工作流,通過對應的邏輯採用命令行工具將開發鏈路串聯起來形成工具鏈
  • 用Docker 構建 Serverless 應用
    用 Docker 構建 Serverless 應用 Serverless 不意味著沒有伺服器,而是從應用可以在一個抽象層上忽略它的存在,而只關注在功能實現上和自身的請求處理上;每一個功能實現在不是單純的業務邏輯處理的代碼,相反每個功能調用具有了 Server 的特質,進化成為了一個具有自省
  • 自創個性化藝術字其實很簡單:在手寫字基礎上藝術化加工
    第二步,將紙上手寫字「杏林職苑」進行數位化,得到背景透明的「杏林職苑「PNG圖片。方法一,用手機將手寫字拍照傳到電腦再進行處理,具體操作略。方法二,Macbook自帶應用「預覽」的攝像頭掃描手寫字。點擊「預覽」應用頂行的「文件」,打開任意一個PDF文件(我用的是事先準備好的空白的PDF文件),點擊右上角「標記」,然後點擊圖中標紅「2「下面那個手寫字樣的按鈕,即可打開手寫字掃描的電子版「杏林職苑「(背景是透明的),點擊它,即可插入PDF文件中。3. 將PDF文件中的「杏林職苑」複製到PPT中,右鍵點擊PPT中的「杏林職苑「另存為PNG圖片。
  • Serverless Framework 2.18.0 發布 - OSCHINA - 中文開源技術交流...
    Serverless 架構開發框架 Serverless Framework 發布了 2.18.0 版本,該框架使用 AWS Lambda、Azure Functions、Google CloudFunctions 等技術,可以構建 Serverless 架構的 Web、移動和 IoT 應用
  • 用CSS如何實現單行圖片與文字垂直居中
    首頁 > 教程 > 關鍵詞 > CSS最新資訊 > 正文 用CSS如何實現單行圖片與文字垂直居中
  • AWS發布新一代Amazon Aurora Serverless
    然後,為了將更多生產級別工作負載運行在Aurora Serverless上,客戶需要其資料庫容量可以更快速、精確地擴容,同時利用Amazon Aurora的全部功能,包括使用多個AWS可用區(AZ)來實現高可用性、全球資料庫來實現低延遲、只讀副本來實現高性能、回溯來實現高彈性、以及並行查詢以加快查詢速度。
  • 騰訊 IMWEB 前端團隊一站式 Serverless 開發解決方案
    學習成就夢想,我們希望能用技術改變教育,改變世界。1) 上手成本高首先有不小的學習成本,像雲函數配置文件,雲函數官網界面操作學習成本,實際使用時,由於雲函數網關 API 連結過長、域名限制等,需要配置 nginx,用特定域名訪問雲函數網關
  • serverless初識-無伺服器無域名怎麼寫一個雲上服務
    簡介無需伺服器,不花錢簡單的幾句代碼本文將用圖解的方式,一步步的創建一個serverless小服務,除了下面列子中的使用方式,還有其他的使用方式hello ok"}) } return response點擊保存,再點擊測試,如果全是按照上面步驟來的話,測試結果如下歐克,到這裡證明函數已經沒啥問題了,接下來配置api網關訪問點擊左邊菜單裡面的觸發管理,如下操作,點擊提交歐克,函數觸發公網地址拿到了,接下來就直接用這個地址吧
  • 用Python快速實現圖片的風格遷移
    我將在這篇博客帶領大家學習如何使用Python來快速實現圖片的風格遷移。閱讀完本博客後,相信你也能夠創造出漂亮的藝術品。什麼是圖片的風格遷移?所謂圖片風格遷移,是指利用程序算法學習著名畫作的風格,然後再把這種風格應用到另外一張圖片上的技術。
  • 支持多語言:Serverless雲函數如何解鎖語言限制?
    雖然我們已經儘量將使用方法和接口設計得簡單,但對於一些用戶來說,仍然要處理程式語言上的接口依賴,以及學習如何正確使用這些接口 API。 5. 維護成本 簡單來說, 如果復用現有方案,給 SCF 提供多語言支持其實是可行的,但是需要給每一種語言編寫一套使用 SCF 的 API。
  • 教你一招:如何用古詩詞優雅地「懟人」,分分鐘變成文藝青年!
    有的人問,學詩詞有用嗎?如何以世俗的眼光來看,詩詞既不能給我們柴米油鹽的生活,也不能給我們帶來豐富的物質生活。古往今來那些詩人,他們寫詩作詩,無非是為了尋求一份心靈的寄託,一種情感的宣洩。可以說詩詞可以帶給我們富足的精神世界,讓我們變得更加的睿智和開明。
  • ImageDT圖匠數據的圖片拼接技術落地應用,高效實現企業賦能
    隨著計算機視覺、機器學習技術的快速發展,全景圖拼接技術已經逐漸可以應用在更多的實際場景中。 試想一下,無論是通過智慧型手機、數位相機或其他攝像設備所進行的圖像採集動作,獲得的圖片往往只能提供一個特定解析度的圖像,並不足以捕捉大的全景,特別是在一個巨大的場景中。所以,我們可以做的是捕捉一個場景中的局部圖像,然後通過全景圖拼接技術合成原始場景圖像。
  • 別太藝術化
    她的名字之中有一個字是寫錯的,估計平時寫字用行書,而寫在封面時工整一些,才露出馬腳來。我本想當她的面告訴她,但想到她一定會十分尷尬,甚至陪我聽這一節課都會是一種煎熬。正好那是最後一節課,下課時,我在一張紙條上把那個字的正確寫法遞給了她。她看了紙條一眼,馬上明白了什麼意思,頓時滿臉通紅。等我們上車回去前,就再也沒有見到她。
  • 全球首發1毫秒計費模式,騰訊雲引領Serverless時代新標準
    使用者可以真正實現按需付費,徹底消除了困擾用戶的資源成本浪費難題。兩大生態打通,構建開源新標準為持續降低開發門檻,推動Serverless大規模應用,騰訊雲構建起完善的生態。比如,在微信生態,推出的「小程序·雲開發」產品,由於可以大幅提升小程序的開發效率,上線一年後註冊帳戶即超過23萬,為超過50萬開發者提供服務。
  • PPT如何用好圖片?
      PPT如何用好圖片?本期分為5大部分去講如何用好圖片。    如何下載到好的圖片PPT裡如何應用?用表格填充底紋就可以快速到達這個效果。步驟:①插入3*3的表格;②插入圖片並複製;③表格右鍵選擇填充效果(圖片或紋理填充),選擇圖片源來自剪貼板;④發現每個單元格都填充一次圖片,這時候勾選圖片平鋪為紋理;⑤修改對齊方式(靠左);⑥右鍵另存為圖片,保存類型為增強型Windows 元文件(*emf)格式;⑦插入另存為的圖片
  • 如何用給圖片加「豎版文字」?教你手機四步實現
    之前以為這類豎版字大家都了解,後來發現不少夥伴要麼真不知道如何把文字「變豎版」,要麼不曉得怎麼用豎版文字讓圖片更有情調。,我們再來看看如何給照片加「豎版」字,非常簡單,手機3步搞定。一、用什麼APP:黃油相機——一款超實用的「加字神器」,經常關注小月手機後期教程的夥伴應該非常熟悉,可以實現很多加字,留白,圖案的效果。二、手機如何製作?
  • Serverless 架構下 Python 輕鬆搞定圖像分類
    通常情況下,這些圖像識別或者分類的工具,都是在客戶端進行數據採集,在服務端進行運算獲得結果,也就是說一般情況下都是有專門的 API 實現圖像識別的。通過該依賴的官方文檔我們可以看到這樣的描述:ImageAI 是一個 python 庫,旨在使開發人員能夠使用簡單的幾行代碼構建具有包含深度學習和計算機視覺功能的應用程式和系統。ImageAI 本著簡潔的原則,支持最先進的機器學習算法,用於圖像預測、自定義圖像預測、物體檢測、視頻檢測、視頻對象跟蹤和圖像預測訓練。
  • 開箱即用,Knative 給您極致的容器 Serverless 體驗
    作者 | 冬島 阿里巴巴技術專家導讀:託管 Knative 開箱即用,您不需要為這些常駐實例付出任何成本。結合 SLB 雲產品提供 Gateway 的能力以及基於突發性能型實例的保留規格功能,極大的節省您的 IaaS 開支,您支付的每一分錢都沒有浪費。
  • 居然之家原創藝術家Art Home開業 探索家居賣場藝術化
    居然之家原創藝術家Art Home開業 探索家居賣場藝術化來源:聯商網2018-06-02 14:09聯商網消息:讓原創藝術走進千家萬戶,成為居然之家新的著力點。這一項目是居然之家探索家居賣場藝術化的新嘗試,在家居行業內也屬首創。據《聯商網》了解,「原創藝術家」以北五環靚屋和十裡河店為樣板間,待商業模式成熟後,將會在全國各地陸續鋪開。