好用的HAI-GPU:我将腾讯云HAI的AI绘画接入小程序

news/2024/9/20 18:37:27

前言

感觉已经进入全面AIGC的时代了,从刚开始的ChatGPT的生成文本,到GPT-4文本到图片的发展,深刻感受到了技术的日新月异。但是GPT-4一直是付费模式,我才开始接触stable diffusion,在自己的电脑上学习AI绘画。

AI绘画的文生图还没研究透彻呢,文生视频sora又来了。对于我来说,学习的速度远远追不上技术的发展,所以索性就沉下心来,深入地学习一下AI绘画。

如今阿里云、腾讯云等很多厂商,都提供了AI绘画地服务能力。通过购买部署AI绘画服务的实例,让Stable Diffusion实现云化。今天就来研究一下腾讯云AI绘画服务hai。

于是,学了一下午的微信小程序开发(有前端开发基础),实现了将hai服务接入到微信小程序。

 操作动图演示:

 

HAI

hai是腾讯云的高性能应用服务(Hyper Application Inventor),在腾讯云的hai服务页面,春季活动1元可以10元抵扣券。

10元可以购买8小时(1.2元/时)的GPU基础套餐。

 在实例中预装了stable diffusion的webui和comfyui,我个人还是比较喜欢webui,喜欢工作流的可以选择后者。

然后就是1元下单购买10元抵扣券。

 

创建实例,使用抵扣券开通8小时体验时间。

 购买之后创建实例,分配公网IP。

这样,实例的创建就完成了,要注意的是:每次重启实例公网IP都会重新分配。点击右侧的WebUI就可以进入Stable Diffusion的webui,体验图生图和文生图的功能。
 

 至此,实例创建完成。我们想要在微信小程序中实现文生图的功能,那么就要调用hai的api,所以必须开启hai的api功能。

 

开放API

根据Hai操作文档,首先开启Stable diffusion服务的api支持。通过实例页面右侧的JupyterLab进入Terminal,执行下面命令监听服务。

cd stable-diffusion-webui
python launch.py --nowebui --xformers --opt-split-attention  --listen --port 7862

执行命令之后,成功监听7862端口。

在实例页面可以看到公网IP,通过http://公网IP:7862/docs可以查看api文档。

我们小程序开发开发的文生图功能,主要是调用txt2img的api,查看接口参数。

然后添加安全组。

至此,关于hai服务的部分已经全部准备完成,接下来就是微信小程序的设计。

微信小程序

我们根据HAI提供的api的请求参数,参考stable diffusion web ui来实现一个简易版的微信小程序。

设计

先看hai文档中api接口的参数。

在上面的参数中,有一些可以设置为固定值,所以我们只挑选一些主要的参数,放到小程序页面的设计中,例如:prompt、negative_prompt、steps、seed(其实也可以不用),sampler_index。batch_size我们默认就生成一张就行了。

对于微信小程序的开发,我是基于uni-app,使用vue进行的开发。UI没有使用市面上基于uni-app封装的组件,我使用的是uni-app提供的原生组件。

开发

在HBuilderX和微信开发者工具中完成开发工作。先实现功能,再优化UI样式,初版的设计如下:

1. prompt

使用textarea组件实现prompt文本框。

<view class="uni-title uni-common-pl">Text Prompt</view>
<view class="uni-textarea"><textarea v-model="prompt" placeholder-class='placeholder' placeholder="请输入提示词" />
</view>
<view class="uni-title uni-common-pl">Text Prompt</view>
<view class="uni-textarea"><textarea v-model="negative_prompt" placeholder-class='placeholder' placeholder="请输入反向提示词" />
</view>

使用v-model与textarea的value实现双向绑定。

import {ref} from 'vue'
const prompt = ref('')
const negative_prompt = ref('')

prompt和negative_prompt的初始值都设置为空,两个变量会随着textarea输入的值而改变,这样就可以获取用户输入的参数。

2. step

step就是Sampling steps(采样步长),就是在reverse diffusion中,从噪声图降噪成正常图片的步数,通常使用20 - 30,这里使用了滑动选择器slider组件来实现。

<view class="uni-title">step</view>
<view><slider class="slider" value="20" activeColor='#465CFF' block-color="#8A6DE9" block-size="20" @change="stepChange" min="10" max="50" show-value />
</view>

activeColor用来设置滑块左侧已选择部分的线条颜色,右侧默认为白色。block-colorblock-size分别设置滑动块的颜色和大小。

change属性绑定回调函数stepChange,当滑动组件改变时就会触发。

const step = ref(20)const stepChange = function(event){step.value = event.detail.value
}

定义step默认值为20,在stepChange获取最新值,我个人比较喜欢设置为30。

3. scale

scale是promot的引导系数,用来控制模型遵循你输入的prompt有多少,用1-30表示。1表示完全忽略你的提示,3表示更有创造力,7为推荐值,表示在自由和遵从之前平衡,15更加遵循提示,30完全遵循提示。

所以说scale使用默认7就行了,但是这里还是尊重选择,使用了滑动选择器slider组件来实现。

<view class="uni-title">scale</view>
<view><slider value="7" activeColor='#465CFF' block-color="#8A6DE9" block-size="20" @change="scaleChange" min="1"max="30" show-value />
</view>

同样这里change绑定回调函数scaleChange

const scale = ref(7)
const scaleChange = function(event){scale.value = event.detail.value
}

定义scale默认值为7,这里不建议修改,在stepChange获取最新值。

4. seed

seed就是随机数种子,在提示词以及其他设置相同时,seed相同才能生成一样的图片。

<view class="uni-title">seed</view>
<input class="uni-input" v-model="seed">

同样在input组件中使用v-model实现双向绑定seed

const seed = ref(-1)

seed默认值为-1。

5. sampler

采样器使用radio组件实现,遍历samplerItem进行渲染。

<view class="uni-title">sampler</view>
<view class="sampler"><radio-group v-for="(item, index) in samplerItem" :key="item.name" @change="samplerChange"><view class="sampler-radio"><radio :value="item.name" :checked="item.name == sampler" /><span>{{ item.name }}</span></view></radio-group>
</view>

定义samplerItem,定义了6个Item,这里只写了2个。

const samplerItem = reactive([{"index": 0,"name": "Euler",},{"index": 1,"name": "LMS",},
])

name是渲染radio的名称,index是下标。默认radio都是垂直分布的,我想让6个radio分成两行,所以就需要使用flex分布。

.sampler {display: flex;flex: 0 0 auto;flex-wrap: wrap;width: 100%;justify-content: center;}.sampler-radio {width: 33vw;
}

同时flex: 0 0 auto属性让弹性元素不发生伸缩,每个radio占用33%的宽度,一行就实现了3个radio的分布。flex-wrap允许换行,所以就将6个radio分布在了两行。

在使用radio你会发现两个问题:

  1. radio一旦选中,是没办法点击取消选中的
  2. 可以多选,但是我们的需求只能单选

为了解决这两个问题,需要定义checked属性来决定是否选中。定义sampler变量用来与当前radio比较。默认是0,所以在第一次进入或刷新小程序时,默认选中第一个radio。

定义radio-group的change事件,当点击选中新的radio时就会触发,这时修改sampler为最新选中的radio,其他radio的checke属性就为false,就取消了选中。

const sampler = ref('Euler')const samplerChange = function(event) {sampler.value = event.detail.value
}

效果如下:

同时,sampler也作为请求参数来请求接口。

6.生成按钮

然后就是生成图片按钮,点击按钮就发起hai接口请求。

<view><button type="primary" @click="generatePic" :loading="isLoading">生成图片</button>
</view>

这里绑定了点击事件generatePic发起请求,接着看如何实现请求函数。

const isPicShow = ref(false)
const isLoading = ref(false)
const picBase64 = ref('')const generatePic = function() {isLoading.value = trueuni.request({url: 'http://公网IP:7862/sdapi/v1/txt2img',method: 'POST', data: {"denoising_strength": 0,"prompt": prompt.value,"negative_prompt": negative_prompt.value,"seed": seed.value,"batch_size": 1,"n_iter": 1,"steps": step.value,"cfg_scale": 7,"width": 512,"height": 512,"restore_faces": false,"tiling": false,"sampler_index": sampler.value},success: (res) => {picBase64.value = `data:image/png;base64,${res.data['images'][0]}`isPicShow.value = trueisLoading.value = true}});
}

使用uni-app的request接口发起请求,请求参数一部分是通过我们定义的组件输入获取的,一部分是固定的。在我们发起请求成功之后,hai服务就会返回一个数组,base64格式的图片就包含在数组之中,如图所示:

我们在success回调函数中对自定义的picBase64isLoadingisPicShow变量做处理。

picBase64用于接收接口获取的base64格式的图片,当img组件的src使用base64而非路径时,需要在图片base64格式之前加上前缀:data:image/png;base64,

isLoading的作用是什么呢? 因为Stable diffusion生成图片是需要时间的,所以在点击按钮之后等待的过程中,给人的感觉是微信小程序卡了。所以就会不停地点击生成按钮,后台就会一直在生成图片。

上图为后台日志,我们可以看到来自同一个IP的多个文生图任务在运行,就导致了资源的浪费。button组件的loading属性就是,当为true的时候是就会一直处于loading动画,不可点击,在generatePic函数发起请求时,设置为true处于加载不可点击状态。当请求完成后,设置为false。

7. image组件

isPicShow用来展示图片组件,当请求完成之后才会显示图片组件。所以在生成按钮的下方,要定义一个图片组件。

<view v-if="isPicShow"><image class="controls-play img" :src="picBase64" @longpress="longpress"></image>
</view>
保存图片

可以看到picBase64绑定在img的src属性上,父组件view用isPicShow来控制是否显示。接着我想长按图片就可以保存到本地相册,所以在image组件上绑定了长按事件longpress

const longpress = function() {uni.showActionSheet({itemList: ['保存图片'],success: (res) => {if (res.tapIndex === 0) {saveBase64ImageToAlbum(picBase64.value);}}});
}

使用uni-app的showActionSheet从底部弹出输出选项框,选择保存图片就可以调用saveBase64ImageToAlbum函数,将base64图片保存到相册中。

function base64ToTempFilePath(base64Data) {return new Promise((resolve, reject) => {uni.getFileSystemManager().writeFile({filePath: `${wx.env.USER_DATA_PATH}/temp-image.png`,data: base64Data,encoding: 'base64',success: () => {resolve(`${wx.env.USER_DATA_PATH}/temp-image.png`);},fail: reject});});}async function saveBase64ImageToAlbum(base64Data) {const tempFilePath = await base64ToTempFilePath(base64Data);uni.saveImageToPhotosAlbum({filePath: tempFilePath,success: () => {uni.showToast({title: '图片保存成功',icon: 'success'});},fail: (error) => {uni.showToast({title: '保存失败:' + error.errMsg,icon: 'none'});}});
}

saveBase64ImageToAlbum的逻辑是将base64格式转换成png格式,存放在用户临时目录中,然后通过调用uni-app的saveImageToPhotosAlbum接口保存到相册中,在开发者工具中演示结果如下:

查看保存的图片:

预览图片

在完成整个微信小程序的开发之后,突然想着没有点击预览图片的功能,于是给image组件绑定了click事件,绑定previewImage函数实现了这一块功能。

const previewImage = function() {base64ToTempFilePath(picBase64.value);uni.previewImage({urls: [`${wx.env.USER_DATA_PATH}/temp-image.png`],current: 0});
}

使用uni-app的previewImage接口,直接预览保存在用户目录的图片即可。

如图,点击实现预览,至此,从参数输入的设计、请求hai文生图接口以及保存图片到相册的开发就完成了。

优化

上面AI绘画使用的是stable diffusion自带的模型,所以生成的图片没有那么理想,所以大家可以根据hai操作文档,将自己喜欢的模型上传。

从程序开发角度来说,优化内容主要是两个部分,一部分是前端UI设计,一部分是权限的控制,例如一个用户可以请求的次数等等,这里只讲前端UI的优化的部分。

微信小程序的优化内容一是可以增加图生图等模块,二是修改css,更改小程序的UI配色和布局设计,这里主要还是讲css部分。

上图是最终修改后的小程序样式,修改了配色和各个组件的样式,这里就以textarea为例。

.prompt-textarea {border-radius: 4px;background-color: #F1F4FA;height: 80px;width: 95vw;margin: auto;font-size: 13px;box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2);padding-left: 5px;padding-top: 3px;
}

设置了4px的圆角,居中、背景色、阴影等,同时通过padding设置,从视觉上让文本输入是向右偏移,而不是紧贴这输入框边缘。

结语

五分钟,从购买1元”炼丹券“到创建实例、使用stable diffusion服务。总的来说,hai极度简化了stable diffusion的使用难度,不需要自己准备电脑,随时随地可以在电脑或者手机上使用AI绘画。

第一次开发微信小程序,还有许多学习的地方,后续会更新一些关于自学微信小程序内容。

 

原创:腾讯云开发者|叫我阿柒啊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/47439.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

基于Chan-Vese算法的图像边缘提取matlab仿真

1.算法运行效果图预览2.算法运行软件版本 matlab2022a3.部分核心程序% 迭代更新水平集函数 err=[]; for i = 1:Iterssubplot(132) imshow(I1,[])hold on;contour(corn, [0.5 0.5],g);title([边缘提取效果,num2str(i), iterations]);hold off;corn = func_evolution(corn, I1, …

C++获取商店应用(msix应用)桌面快捷方式的安装目录

传统应用的快捷方式目标指向可执行文件的路径,但是对于商店应用(也叫msix打包应用),则指向一个奇怪的字符串,使用IShellLink::GetPath获取路径时,则得到的是空字符串,而我们的最终目的是要拿到应用的安装路径,那该怎么办呢? 首先解释一下,那个奇怪的字符串叫AUMID(App …

原生鸿蒙的成长史中,书写着无数鸿蒙先锋的故事

千帆并进破风云,浩瀚星河耀苍穹。 2024年6月21日,是属于鸿蒙的:HarmonyOS NEXT鸿蒙星河版Beta测试开启,5000多个鸿蒙原生应用已全部启动开发,其中超1500家已完成上架,鸿蒙生态设备数量超9亿,已有254万HarmonyOS开发者投入到鸿蒙世界的开发中,开发者服务调用次数827亿次…

鸿蒙先锋共筑星河 |科技浪潮中的教育革新,看南京大学教授的HarmonyOS教学之路

在科技的浩渺海洋中,每一次技术革新都如同激起的浪潮,推动着社会不断前进。HarmonyOS的崛起,不仅引领移动应用开发领域的新方向,也为高校教育带来了前所未有的机遇。高校老师们通过应用与实践,开展教学和研究工作,培养出一大批具备创新能力的人才。 南京大学教授刘钦,以其敏锐的…

vue样式

1.局部样式,在style标签上加stoped,样式只在当前组件生效,原理是编译后给当前组件内所有标签加上data-v-hash属性,给样式生成属性选择器 .myClass[data-v-hash] {color: red; },不加scoped样式在全局生效。 <style scoped> .myClass {color: red; } </style> …

FUM论文阅读笔记

FUM: Fine-grained and Fast User Modeling for News Recommendation Abstract 现存的问题: ​ 现有方法通常先将用户点击的新闻独立编码为新闻嵌入,然后将其聚合为用户嵌入。然而,这些方法忽略了同一用户点击的不同新闻之间的词级交互,而这种交互包含丰富的细节线索来推断…

从专业领域到代码世界:一位全科医生到全栈开发者的HarmonyOS跨界之旅

2024年6月23日,在华为开发者大会(HDC 2024)HarmonyOS开发者一站式服务分论坛上,汇集了华为专家、各行各业的开发者们,共同探讨如何在HarmonyOS的赋能下为行业和社会创造更多价值。特别要提及其中一位开发者陈胜歌,他于6月22日受邀参加了在东莞三丫坡举办的HDC 2024开发者…