小程序|基于 Serverless 架構的頭像漫畫風處理小程序

小程序|基于 Serverless 架構的頭像漫畫風處理小程序

文章圖片


前言 我一直都想要有一個漫畫版的頭像 , 奈何手太笨 , 用了很多軟件 “捏不出來” , 所以就在想著 , 是否可以基于 AI 實現這樣一個功能 , 并部署到 Serverless 架構上讓更多人來嘗試使用呢?
后端項目 后端項目采用業界鼎鼎有名的動漫風格轉化濾鏡庫 AnimeGAN 的 v2 版本 , 效果大概如下:

關于這個模型的具體的信息 , 在這里不做詳細的介紹和說明 。 通過與 Python Web 框架結合 , 將 AI 模型通過接口對外暴露:
from PIL import Imageimport ioimport torchimport base64import bottleimport randomimport jsoncacheDir = '/tmp/'modelDir = './model/bryandlee_animegan2-pytorch_main'getModel = lambda modelName: torch.hub.load(modelDir \"generator\" pretrained=modelName source='local')models = { 'celeba_distill': getModel('celeba_distill') 'face_paint_512_v1': getModel('face_paint_512_v1') 'face_paint_512_v2': getModel('face_paint_512_v2') 'paprika': getModel('paprika')randomStr = lambda num=5: \"\".join(random.sample('abcdefghijklmnopqrstuvwxyz' num))face2paint = torch.hub.load(modelDir \"face2paint\" size=512 source='local')@bottle.route('/images/comic_style' method='POST')def getComicStyle(): result = { try: postData = https://mparticle.uc.cn/api/json.loads(bottle.request.body.read().decode(/"utf-8\")) style = postData.get(\"style\" 'celeba_distill') image = postData.get(\"image\") localName = randomStr(10) # 圖片獲取 imagePath = cacheDir + localName with open(imagePath 'wb') as f: f.write(base64.b64decode(image)) # 內容預測 model = models[style
imgAttr = Image.open(imagePath).convert(\"RGB\") outAttr = face2paint(model imgAttr) img_buffer = io.BytesIO() outAttr.save(img_buffer format='JPEG') byte_data = https://mparticle.uc.cn/api/img_buffer.getvalue() img_buffer.close() result[/"photo\"
= 'data:image/jpg;base64 %s' % base64.b64encode(byte_data).decode() except Exception as e: print(\"ERROR: \" e) result[\"error\"
= True return resultapp = bottle.default_app()if __name__ == \"__main__\": bottle.run(host='localhost' port=8099) 整個代碼是基于 Serverless 架構進行了部分改良的:



















【小程序|基于 Serverless 架構的頭像漫畫風處理小程序】
實例初始化的時候 , 進行模型的加載 , 已經可能的減少頻繁的冷啟動帶來的影響情況; 在函數模式下 , 往往只有/tmp目錄是可寫的 , 所以圖片會被緩存到/tmp目錄下; 雖然說函數計算是“無狀態”的 , 但是實際上也有復用的情況 , 所有數據在存儲到tmp的時候進行了隨機命名; 雖然部分云廠商支持二進制的文件上傳 , 但是大部分的 Serverless 架構對二進制上傳支持的并不友好 , 所以這里依舊采用 Base64 上傳的方案; 上面的代碼 , 更多是和 AI 相關的 , 除此之外 , 還需要有一個獲取模型列表 , 以及模型路徑等相關信息的接口: import bottle@bottle.route('/system/styles' method='GET')def styles(): return { \"AI動漫風\": { 'color': 'red' 'detailList': { \"風格1\": { 'uri': \"images/comic_style\" 'name': 'celeba_distill' 'color': 'orange' 'preview': 'https://serverless-article-picture.oss-cn-hangzhou.aliyuncs.com/1647773808708_20220320105649389392.png'\"風格2\": { 'uri': \"images/comic_style\" 'name': 'face_paint_512_v1' 'color': 'blue' 'preview': 'https://serverless-article-picture.oss-cn-hangzhou.aliyuncs.com/1647773875279_20220320105756071508.png'\"風格3\": { 'uri': \"images/comic_style\" 'name': 'face_paint_512_v2' 'color': 'pink' 'preview': 'https://serverless-article-picture.oss-cn-hangzhou.aliyuncs.com/1647773926924_20220320105847286510.png'\"風格4\": { 'uri': \"images/comic_style\" 'name': 'paprika' 'color': 'cyan' 'preview': 'https://serverless-article-picture.oss-cn-hangzhou.aliyuncs.com/1647773976277_20220320105936594662.png'app = bottle.default_app()if __name__ == \"__main__\": bottle.run(host='localhost' port=8099) 可以看到 , 此時我的做法是 , 新增了一個函數作為新接口對外暴露 , 那么為什么不在剛剛的項目中 , 增加這樣的一個接口呢?而是要多維護一個函數呢? AI 模型加載速度慢 , 如果把獲取AI處理列表的接口集成進去 , 勢必會影響該接口的性能; AI 模型所需配置的內存會比較多 , 而獲取 AI 處理列表的接口所需要的內存非常少 , 而內存會和計費有一定的關系 , 所以分開有助于成本的降低; 關于第二個接口(獲取 AI 處理列表的接口) , 相對來說是比較簡單的 , 沒什么問題 , 但是針對第一個 AI 模型的接口 , 就有比較頭疼的點: 模型所需要的依賴 , 可能涉及到一些二進制編譯的過程 , 所以導致無法直接跨平臺使用; 模型文件比較大 (單純的 Pytorch 就超過 800M) , 函數計算的上傳代碼最多才 100M , 所以這個項目無法直接上傳; 所以這里需要借助 Serverless Devs 項目來進行處理: 參考 https://www.serverless-devs.com/fc/yaml/readme 完成 s.yaml 的編寫: edition: 1.0.0name: start-aiaccess: \"default\"vars: # 全局變量 region: cn-hangzhou service: name: ai nasConfig: # NAS配置 配置后function可以訪問指定NAS userId: 10003 # userID 默認為10003 groupId: 10003 # groupID 默認為10003 mountPoints: # 目錄配置 - serverAddr: 0fe764bf9d-kci94.cn-hangzhou.nas.aliyuncs.com # NAS 服務器地址 nasDir: /python3 fcDir: /mnt/python3 vpcConfig: vpcId: vpc-bp1rmyncqxoagiyqnbcxk securityGroupId: sg-bp1dpxwusntfryekord6 vswitchIds: - vsw-bp1wqgi5lptlmk8nk5yi0services: image: component: fc props: # 組件的屬性值 region: ${vars.region service: ${vars.service function: name: image_server description: 圖片處理服務 runtime: python3 codeUri: ./ ossBucket: temp-code-cn-hangzhou handler: index.app memorySize: 3072 timeout: 300 environmentVariables: PYTHONUSERBASE: /mnt/python3/python triggers: - name: httpTrigger type: http config: authType: anonymous methods: - GET - POST - PUT customDomains: - domainName: avatar.aialbum.net protocol: HTTP routeConfigs: - path: /* 然后進行: 1、依賴的安裝:s build --use-docker 2、項目的部署:s deploy 3、在 NAS 中創建目錄 , 上傳依賴: s nas command mkdir /mnt/python3/pythons nas upload -r 本地依賴路徑 /mnt/python3/python 完成之后可以通過接口對項目進行測試 。另外 , 微信小程序需要 https 的后臺接口 , 所以這里還需要配置 https 相關的證書信息 , 此處不做展開 。小程序項目 小程序項目依舊采用 colorUi , 整個項目就只有一個頁面: 頁面相關布局:scroll-view scroll-y class=\"scrollPage\"image src='https://mparticle.uc.cn/images/topbg.jpg' mode='widthFix' class='response'/imageview class=\"cu-bar bg-white solid-bottom margin-top\"view class=\"action\"text class=\"cuIcon-title text-blue\"/text第一步:選擇圖片/view/viewview class=\"padding bg-white solid-bottom\"view class=\"flex\"view class=\"flex-sub bg-grey padding-sm margin-xs radius text-center\" bindtap=\"chosePhoto\"本地上傳圖片/viewview class=\"flex-sub bg-grey padding-sm margin-xs radius text-center\" bindtap=\"getUserAvatar\"獲取當前頭像/view/view/viewview class=\"padding bg-white\" hidden=\"{{!userChosePhoho\"view class=\"images\"image src=https://mparticle.uc.cn/"{{userChosePhoho\" mode=\"widthFix\" bindtap=\"previewImage\" bindlongpress=\"editImage\" data-image=\"{{userChosePhoho\"/image/viewview class=\"text-right padding-top text-gray\"* 點擊圖片可預覽 , 長按圖片可編輯/view/viewview class=\"cu-bar bg-white solid-bottom margin-top\"view class=\"action\"text class=\"cuIcon-title text-blue\"/text第二步:選擇圖片處理方案/view/viewview class=\"bg-white\"scroll-view scroll-x class=\"bg-white nav\"view class=\"flex text-center\"view class=\"cu-item flex-sub {{style==currentStyle?'text-orange cur':''\" wx:for=\"{{styleList\" wx:for-index=\"style\" bindtap=\"changeStyle\" data-style=\"{{style\"{{style/view/view/scroll-view/viewview class=\"padding-sm bg-white solid-bottom\"view class=\"cu-avatar round xl bg-{{item.color margin-xs\" wx:for=\"{{styleList[currentStyle

相關經驗推薦