ITPub博客

首页 > 应用开发 > IT综合 > 如何在移动应用中实现AI画图?

如何在移动应用中实现AI画图?

原创 IT综合 作者:比亚的答案 时间:2023-04-11 16:09:58 0 删除 编辑

最近AIGC 简直是杀疯了,领导动不动就让我们在APP 里引入大语言模型,引入AI画图……说搞就搞!本期基于最近在app 里引入AI画图小程序的操作,给大家做一波实践分享。

Scribble Diffusion 是一个简单的在线服务,它使用 AI 将粗略的草图转换为精致的图像,每一张图像都是不同的(而且没有版权困扰)。简单来说,我们只需要「用画笔描绘一张草图,在输入描述后稍等片刻」,随后就会为你生成一幅画。这幅画可以多次生成,每次生成的结果也都大不相同。

Scribble Diffusion 的能力大概是这样的(左边是我画的,右边是 TA 画的)

a photo of grassland with cloud(有云朵的草地) the sun setting behind the mountains(山后落日) A lovely kitten(小猫咪呀)

我发现 Scribble Diffusion 作画的能力非常出乎意料,而且可以根据你的描述来定义不同的照片风格(比如照片,油画,素描等等),于是就产生了把这个AI 画图小程序内嵌到公司App中的想法。另外把小程序内嵌App是通过 FinClip 容器技术实现的。(毕竟开箱即用,也不需要做什么配置)。

调研官网之后发现官网中的元素非常简单,正因如此,我觉得把「Scribble Diffusion」搬运到 小程序里大概要分这样几步:

  1. 使用 canvas 实现画板,能够在小程序中进行绘画;
  2. 提供输入框与生成按钮,能够补充图片的描述和生成的按钮;
  3. 获取生成的图片链接进行展示;

像那些如何写代码,账号注册和创建小程序的流程,各位看官可以看这里:

从零到一,我也能写小程序

看了这篇文章,即使让我现在就从头写一个能够正常运行的小程序,也没有原本想象中的那么难了。  

使用小程序实现画板

我们可以使用小程序来实现一个画板,使用 canvas 标签实现画笔功能。用户可以在画板上绘制画作,也可以选择清空画板操作。

下面是一个示例代码:

<!--画布区域--> <view>     <canvas id="myCanvas" canvas-id="myCanvas"         disable-scroll="false"         bindtouchstart="touchStart"         bindtouchmove="touchMove"         bindtouchend="touchEnd">     </canvas> </view> <view bindtap="reset">   清空画板 </view>
Page({   data: {     isProcessing: false,     prompt: '',     scribble: null,     pen : 2, //画笔粗细默认值     color : '#000000', // 画笔颜色默认值     result: null,     text: ''   },   startX: 0, //保存X坐标轴变量   startY: 0, //保存X坐标轴变量   onLoad(params) {     wx.createSelectorQuery().select('#myCanvas').context((res) => {       this.context = res.context     }).exec()   },   //手指触摸动作开始   touchStart: function (e) {       //得到触摸点的坐标       this.startX = e.changedTouches[0].x       this.startY = e.changedTouches[0].y       // this.context = wx.createContext()       this.context.setStrokeStyle(this.data.color)       this.context.setLineWidth(this.data.pen)       this.context.setLineCap('round') // 让线条圆润        this.context.beginPath()   },   //手指触摸后移动   touchMove: function (e) {       var startX1 = e.changedTouches[0].x       var startY1 = e.changedTouches[0].y       this.context.moveTo(this.startX, this.startY)       this.context.lineTo(startX1, startY1)       this.context.stroke()       this.startX = startX1;       this.startY = startY1;                       //只是一个记录方法调用的容器,用于生成记录绘制行为的actions数组。context跟<canvas/>不存在对应关系,一个context生成画布的绘制动作数组可以应用于多个<canvas/>       wx.drawCanvas({          canvasId: 'myCanvas',          reserve: true,          actions: this.context.getActions() // 获取绘图动作数组       })   },   //手指触摸动作结束   touchEnd: function () {     var imageData =  wx.canvasGetImageData({       canvasId: 'myCanvas',       height: 250,       width: 250,       x: 0,       y: 0,       success(res){         return res.data       }     })   },   //清除画板   reset: function(){     this.context.clearRect(0, 0, 400, 400);     this.context.draw(true)   } })

提供输入框和生成按钮

我们需要提供一个 input 输入框,供用户输入 prompt;同时,我们需要提供一个按钮,点击时会触发响应事件,将 canvas 内容生成图片,同时将 prompt 输入作为参数,提交给服务端进行图片生成。

这里是示例代码:

<!-- 输入框 --> <view>    <view>      <input type="text" name="go"  placeholder="用关键词描述画的内容" bindinput="update"/>   </view> </view>
Page({   ... 省略上述代码   // 更新表单提交按钮状态   update(e){     this.setData({       prompt : e.detail.value     })   }, })

获取生成的图片链接并展示

当用户点击生成图片按钮后,我们会将 canvas 内容和用户输入的 prompt 作为参数提交给服务端进行图片生成。服务端会返回生成的图片链接,我们需要将它展示给用户。

在下面的示例代码中,我们服务端发送 POST 请求,然后解析返回的 JSON 数据,获取图片链接,并将其添加到页面中。用户就可以看到生成的图片了。

<!-- 绘图结果 --> <view wx:if="{{result}}">   <view>     <view>       <image src="{{result}}" mode="aspectFit" />      </view>     <view>       <view bindtap="download">         下载       </view>     </view>   </view> </view>
Page({   ... 省略上述代码   async getCanvasImage() {     return new Promise((resolve, reject) => {       wx.canvasToTempFilePath({         x: 0,         y: 0,         width: 250,         height: 250,         destWidth: 250,         destHeight: 250,         canvasId: 'myCanvas',         success(res) {           console.log(res.tempFilePath)           resolve(res.tempFilePath)         },         fail(err) {           console.log(err)         }       })     })   },   async upload(image) {     return new Promise((resolve) => {       wx.uploadFile({         url: 'xxxxxx',         filePath: image,         name: 'file',         success (res){           const data = JSON.parse(res.data)           resolve(data.url)         },         fail(err){           console.log('上传失败')           console.log(err)         }       })     })   }, async sleep(time) {     return new Promise((resolve) => {       setTimeout(() => {         resolve()       }, time)     })   },   async handleSubmit(e) {     if (!this.data.prompt) {       return     }     wx.showLoading({       title: '生成中',     })     try {       const prompt = this.data.prompt       const image = await this.getCanvasImage()       this.setData({         error: null,         isProcessing: true       });       const url = await this.upload(image)       console.log('图片', url)       const body = {         prompt: prompt,         image: url,       };       const response = await my_fetch.fetch( {         url: "https://scribblediffusion.com/api/predictions",         method: "POST",         headers: {           "Content-Type": "application/json",         },         params: JSON.stringify(body),       });       let prediction = response.data;       console.log('预测', prediction)       if (response.statusCode !== 201) {         wx.showToast({           title: '生成失败',           duration: 2000         })         this.setData({           error: '生成失败'         });         return;       }       while (         prediction.status !== "succeeded" &&         prediction.status !== "failed"       ) {         console.log(prediction.status)         await this.sleep(500);         const response = await my_fetch.fetch({           url:"https://scribblediffusion.com/api/predictions/" + prediction.id,         });         prediction = response.data;         if (response.statusCode !== 200) {           this.setData({             error: prediction.detail           });           return;         }       }       if (Array.isArray(prediction.output) && prediction.output.length > 1) {         wx.hideLoading()         this.setData({           isProcessing: false,           result: prediction.output[1]         });       } else {         wx.hideLoading()         wx.showToast({           title: '生成失败',           duration: 2000         })         this.setData({           isProcessing: false,           error: '生成失败'         })       }      } catch (error) {       wx.hideLoading()       console.log(error)       wx.showToast({         title: '生成失败',         duration: 2000       })      }   }, })

生成完小程序之后,再通过  FinClip 上传小程序就可以在 App 获得画板功能啦!我把这个小程序上传到了 「FinClip 小程序应用市场」中,你可以扫描下方的二维码随意体验,总的来说,还是挺好玩的。

如果你对小程序与 AI 的结合有什么奇思妙想,欢迎留言探讨!

当然,如果你对  Scribble Diffusion 有更多奇怪的想法想付诸实践,开发者也已经将项目文件进行了开源,欢迎尝试~


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70017183/viewspace-2945016/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2022-04-27

  • 博文量
    139
  • 访问量
    87220