一、前言
最近 AIGC 火出圈了还记得上次在群里面的大佬们聊到了 AIGC 的话题我滴妈涉及到我的知识盲区了知识听过这个东西就是生成图片所以不怎么感冒.然后最近腾讯云开发者社区发布了腾讯云AI技术专家与传智教育人工智能学科高级技术专家正在联合打造《腾讯云AI绘画-StableDiffusion图像生成》训练营,训练营将通过8小时的学习带你玩转AI绘画的教程我也就去深入了一下. 感兴趣的大佬快来报名学习呀~
点击报名,抢先学习《腾讯云AI绘画-StableDiffusion图像生成》训练营 八小时玩转AI绘画
💗 本篇文章以
五万三千零三十二字
带您玩转腾讯AI绘画图像生成搭建前后端分离项目如果觉得本篇文章帮助到了您喜欢本篇文章那就点赞支持一下吧!!!!!
💗 本篇教程我也是一边写文章一边写代码,一起来吧!!!!!
技术架构图
技术选型: 使用了前端 Vue3 +Vite来搭建前端架构. 后端使用Java语言的SpringBoot3框架来搭建服务. 最后我会使用语音系统在用户生成完毕图片后生成语音提醒生成成功 (因为如果图片质量较高生成是要有一定的耗时的!!!!)
前端效果演示
注意⚠️: 前端样式仿造腾讯云智能图像创作平台的UI 地址: https://ti-image.cloud.tencent.com/text-to-image/
点击报名,抢先学习《腾讯云AI绘画-StableDiffusion图像生成》训练营
了解 AIGC
AIGC(Artificial Intelligence Generated Content)是人工智能1.0时代进入2.0时代的重要标志。它是指基于生成对抗网络、大型预训练模型等人工智能的技术方法,通过已有数据的学习和识别,以适当的泛化能力生成相关内容的技术。AIGC技术的核心思想是利用人工智能算法生成具有一定创意和质量的内容。通过训练模型和大量数据的学习,AIGC可以根据输入的条件或指导,生成与之相关的内容。例如,通过输入关键词、描述或样本,AIGC可以生成与之相匹配的文章、图像、音频等。
目前训练营更新了五节课,但是都是理论知识点.
在学完之后赠送了我免费的 API 介入和在线平台免费体验的次数各 50 次
接下来我们使用赠送的 API 来自己对接一个随便玩玩,在此之前我们先体验一下在线的
二、体验智能 AI绘画
智能文生图 (TextToImage)
智能文生图(Text-to-Image Generation)是人工智能生成内容(AIGC)的一个主要方向。它是一种前沿的图像生成方法,通过输入文字描述,即可生成相对应的图片。
文生图技术在内容生产等领域有着广泛的应用前景,例如艺术创作、设计、媒体等。
以下是一些常见的应用场景:
- 文生图可以帮助用户快速生成与文字描述相对应的图片,提高内容创作的效率和质量。
- 生成的图片可以用于广告宣传如PPT、短视频、文案、社交媒体宣传图或广告宣传图片等。
- 文生图可以用于制作教育课件、教材插图等帮助学生更好地理解和记忆知识。
智能文生图可以应用于各种需要生成图像的场景,提高生产效率、降低成本
智能文生图风格列表
风格单选(建议):让一张生成图具备某一种风格的特征,为提高效果稳定性,建议选择且只选择一种风格。取值示例:如果您想生成一张水墨画风格的图片,请在下表中找到水墨画对应的风格编号为101,向 Styles 传入:"101"。
风格多选(不建议):实际使用中发现对同一张图片叠加多种风格容易导致生成效果不稳定,因此,为了保障较好的效果,如果您在一次调用中同时传入了多个风格编号,接口将自动取第一个风格编号作为实际输入。例如您传入"101", "102", "103",将只取101。
官方建议我们进行风格的单选,后面我们也就搞单选就完事了
请求频率限制为1次/秒,别搞快了我们就 3 秒一次就完事了
注意⚠️:
如果选择“不限定风格”,模型将不固定生成的风格,大部分情况下将生成写实图片,也可能随机生成其他风格,此时您可以在 prompt 中输入风格特征描述来自定义风格,例如“一只小狗,xx风格”。
我们可以前往腾讯云 AI 绘画功能体验一下腾讯云AI绘画特惠活动每个月拥有 20 次的体验机会
进入页面往下滑找到智能文生图
挂关键词: 3D,程序员在加班改 BUG,在公司
效果、人物、场景
智能图生图 (ImageToImage)
智能图生图是一种利用人工智能技术,根据给定的图片和提示词生成新的图片的过程。在这个过程中,AI会学习参考图片的信息,如颜色、纹理等,并结合提示词生成与参考图片相似但具有不同细节和内容的新图片。
图生图的原理是通过“加噪”和“去噪”的过程,使AI能够从参考图片中获取特征,并将其反映到最终生成的图片中。这使得生成的图片与参考图片具有足够的相似性,同时也具有新的细节和内容。
智能图生图的应用范围广泛,可以用于创作漫画、插画、设计等,也可以用于图像修复、去水印等场景。
智能图生图风格列表
和前面的文生图一样的处理方式,只是传递的风格有限,图生图目前仅支持如下
进入页面往下滑找到智能图生图
好了我们已经了解了文生图和图生图的一些注意事项和参数就行,还是刚才的页面我们直接进去 API 调试
三、调试接入文/图
我们点击产品管理
我们点击接口文档看看咋对接
一般这种腾讯云都会有 API 调试器的所以我们稍微的过一下这个接口文档我们直接去 API 调试器去玩
API 在线 调试
这里调试好直接运行就完事了
我们可以看到输入参数很多但是具体可以实现图片生成的参数就一个其他的都是对其进行优化接下来我们来看看这些参数的意思吧, Prompt
文本描述是必须传递的!!!
我们输入 Prompt 参数 旁边的代码生成器就已经把 Demo 搞好了
我们点击旁边的在线调用按钮发起生成测试 通过API发送请求等同于真实操作,请小心进行
请求成功没毛病返回了 以下的数据结构 其中 ResultImage 就是我们的图片信息,我们访问看看
data:image/jpeg;base64,
加 ResultImage 返回的 base64 数据
"Response": {
"RequestId": "dbee275e-03ad-4040-992c-4b0def7bce05",
"ResultImage": "base64"
}
如果我想返回 URL 的图片信息并且水印自定义
在上面的请求参数我没说 LogoParam
这个参数
他的作用为 标识内容设置。 默认在生成结果图右下角添加“图片由 AI 生成”字样,您可根据自身需要替换为其他的标识图片 LogoParam 里面需要传递的是一个对象 LogoRect
名称 | 类型 | 必选 | 描述 |
---|---|---|---|
X | Integer | 否 | 左上角X坐标 |
Y | Integer | 否 | 左上角Y坐标 |
Width | Integer | 否 | 方框宽度 |
Height | Integer | 否 | 方框高度 |
返回 Url
生成的图片返回给我 URL 形式的,上面我已经说过了给 RspImgType
参数改成 url
他默认是 Base64
返回图像方式(base64 或 url) ,二选一,默认为 base64。url 有效期为1小时
调试进行测试
图片地址: https://cdn.nlark.com/yuque/0/2023/png/2426233/1702196852392-3083990a-0590-4299-b89c-6c915f2b8a7a.png
如果想右下角 计算方法就是 你需要生成的图片大小 - 你水印图片的大小
例如: 768:768 768 - 71 (水印宽) X 轴 、768 - 20 (水印高) Y轴
反正看你想咋调整了随便搞哈哈哈
经过上面的学习、调试我们就已经掌握基本的逻辑了现在我们开始对接搞一个我们自己的服务玩玩
文本生成图片的对接核心代码记得保存一下下哦
图生图和文生图一模一样的操作,只是传递的参数不同我这里就不详细展示了同学们自己动手调试一下下吧~
四、后端搭建
需求:
使用 AIGC 绘画 API 搭建后台服务调用,使用 Java 语言操作
目前我是打算先搞后端把接口都调通打算搞个前端来配合接口来玩
我们使用 SpringBoot3 来直接搭建 Java 应用打开 IntelliJ IDEA 推荐这个嗷其他的不行!!!!
本次使用的是 SpringBoot3+JDK17 最低版本 JDK17 其他的就不行了,你们也可以搭建 SpringBoot2 的都一样
依赖
目前我们就先搞这三个其他的后面在加
我这里就修改了下配置文件的格式我喜欢 DSL 格式的看你们自己
编写测试类
添加坐标
<!--腾讯云-->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.923</version>
</dependency>
密钥
先去腾讯云控制获取一下密钥信息
密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
@SpringBootTest class TencentAigcApplicationTests {
@Value("${tencent.secretId}") private String secretId; @Value("${tencent.secretKey}") private String secretKey; final Credential cred = new Credential(secretId, secretKey); @Test void contextLoads() throws TencentCloudSDKException { // 实例化一个http选项,可选的,没有特殊需求可以跳过 HttpProfile httpProfile = new HttpProfile(); httpProfile.setEndpoint("aiart.tencentcloudapi.com"); // 实例化一个client选项,可选的,没有特殊需求可以跳过 ClientProfile clientProfile = new ClientProfile(); clientProfile.setHttpProfile(httpProfile); // 实例化要请求产品的client对象,clientProfile是可选的 AiartClient client = new AiartClient(cred, "ap-shanghai", clientProfile); TextToImageRequest req = new TextToImageRequest(); // 根据文本描述生成对应的图片 req.setPrompt("3D,帮我生成一个在海边跳舞的小女孩"); // 图片的比例大小 ResultConfig resultConfig1 = new ResultConfig(); resultConfig1.setResolution("1920:1080"); req.setResultConfig(resultConfig1); // 图片的水印 LogoParam logoParam1 = new LogoParam(); logoParam1.setLogoUrl("https://cdn.nlark.com/yuque/0/2023/png/2426233/1702196852392-3083990a-0590-4299-b89c-6c915f2b8a7a.png"); LogoRect logoRect1 = new LogoRect(); logoRect1.setX(697L); // 比例宽 - 水印的宽度 logoRect1.setY(748L); // 比例高 - 水印的高度 logoRect1.setWidth(71L); // 水印的宽度 logoRect1.setHeight(20L); // 水印的高度 logoParam1.setLogoRect(logoRect1); req.setLogoParam(logoParam1); // 返回的是URL req.setRspImgType("url"); // 返回的resp是一个TextToImageResponse的实例,与请求对象对应 final TextToImageResponse resp = client.TextToImage(req); // 输出json格式的字符串回包 // final String resultImage = "data:image/jpeg;base64," + resp.getResultImage(); System.out.println(TextToImageResponse.toJsonString(resp)); }
}
执行单元测试
我滴妈太可爱了吧
编写文本生成图片接口
接下来我们把我们的单元测试进行改造为工具类
在文件夹下面新增 AIGCUtils
/**
- AIGC 工具 人工智能内容生成
- @author Yang Shuai
- Create By 2023/12/10
*/
@Slf4j
@RequiredArgsConstructor
public class AIGCUtils {// ...... 业务代码
}
新增我们的一个腾讯云密钥配置用来管理和扩展
package com.yby6.tencentaigc.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
- 腾讯云yml配置
- @author Yang Shuai
Create By 2023/12/10
/
@Data
@Component
@ConfigurationProperties(prefix = "tencent")
public class TencentConfig {
private String appId;
private String secretId;
private String secretKey;
}
修改 AIGCUtls 新增请求凭证 - 静态调用写法
编写请求凭证我们使用的是腾讯的 SDK 里面默认给我们把一些鉴权弄好了我们只需要要传递密钥即可
这段代码后面都会用到所以我们直接提取出来就完事了
/
*
获取 腾讯云 AIGC客户请求
*/
public static AiartClient getAiartClient() {
Credential cred = new Credential(aigcUtils.tencentConfig.secretId, aigcUtils.tencentConfig.secretKey);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
// api地址
httpProfile.setEndpoint("aiart.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
return new AiartClient(cred, "ap-shanghai", clientProfile);
}
可能有大佬发现了我们这个方法是静态的所以我们直接使用 IOC 当中的 配置类是行不通的所以使用PostConstruct 来绕过静态
要在静态方法里面调用IOC容器的bean对象 PostConstruct 在构造函数执行之后执行。可以方便的把注入的bean对象给到静态属性
IOC 调用
就是使用的时候需要编写去 IOC 容器拿 Bean 的步骤
package com.yby6.tencentaigc.utils;
import com.tencentcloudapi.aiart.v20221229.AiartClient;
import com.tencentcloudapi.aiart.v20221229.models.*;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.yby6.tencentaigc.config.TencentConfig;
import com.yby6.tencentaigc.domain.TextToImage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/**
AIGC 工具 人工智能内容生成
@author Yang Shuai
Create By 2023/12/10
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class AIGCIOCUtils {private final TencentConfig tencentConfig;
/**
- 获取 腾讯云 AIGC客户请求
*/
public AiartClient getAiartClient() {
Credential cred = new Credential(tencentConfig.getSecretId(), tencentConfig.getSecretKey());
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
// api地址
httpProfile.setEndpoint("aiart.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
return new AiartClient(cred, "ap-shanghai", clientProfile);}
}
简单测试下不同方式
我这里给大家测试了一下都是 ok 的没毛病的,看大家喜欢哪一种方式了我比较喜欢静态的懒得再去 IOC 拿了
封装文本生成图片方法
看看我们原先写的单元测试代码 也就组装请求参数的这个地方自定义一下 前端来传递参数就行
前面我们看到了那么多的请求参数,实际上我们经过测试也就这些满足我们的需求了
// 根据文本描述生成对应的图片
req.setPrompt("3D,帮我生成一个在海边跳舞的小女孩,还里面有可爱的鱼群,天空上飞舞的百灵鸟");// 图片的比例大小
ResultConfig resultConfig1 = new ResultConfig();
resultConfig1.setResolution("1920:1080");
req.setResultConfig(resultConfig1);// 图片的水印
LogoParam logoParam1 = new LogoParam();
logoParam1.setLogoUrl("https://cdn.nlark.com/yuque/0/2023/png/2426233/1702196852392-3083990a-0590-4299-b89c-6c915f2b8a7a.png");
LogoRect logoRect1 = new LogoRect();
logoRect1.setX(697L); // 比例宽 - 水印的宽度
logoRect1.setY(748L); // 比例高 - 水印的高度
logoRect1.setWidth(71L); // 水印的宽度
logoRect1.setHeight(20L); // 水印的高度
logoParam1.setLogoRect(logoRect1);
req.setLogoParam(logoParam1);
// 返回的是URL
req.setRspImgType("url");
在 com.yby6.tencentaigc
下面新增文件夹 domain
在新增 TextToImage
接收前端信息实体类
我进行了单独的处理了一下字段信息
实际上我们需要前端传递的参数为:
文本描述: prompt
反向文本描述: negativePrompt
生成图片的风格: styles 这个腾讯 sdk 是一个字符串数组但是官方建议单个所以我们就直接传递单个就行
生成的图片分辨率: resolution 这个参数看下面的注释哦,根据前端选择对应的分辨率来生成
返回的格式: rspImgType url or base64。这个我就在代码层面钉死了为 url 其实这个也可以不要
package com.yby6.tencentaigc.domain;
import lombok.Data;
/**
文本转图像参数类
@author Yang Shuai
Create By 2023/12/10
*/
@Data
public class TextToImage {/**
- 文本描述。
- 算法将根据输入的文本智能生成与之相关的图像。建议详细描述画面主体、细节、场景等,文本描述越丰富,生成效果越精美。
- 不能为空,推荐使用中文。最多可传256个 utf-8 字符。
*/
private String prompt;/**
- 反向文本描述。
- 用于一定程度上从反面引导模型生成的走向,减少生成结果中出现描述内容的可能,但不能完全杜绝。
- 推荐使用中文。最多可传256个 utf-8 字符。
*/
private String negativePrompt;/**
- 绘画风格。
- 请在 智能文生图风格列表 中选择期望的风格,传入风格编号。
- 推荐使用且只使用一种风格。不传默认使用201(日系动漫风格)。
*/
private String styles = "201";/**
- 生成图结果的配置,包括输出图片分辨率和尺寸等。
- <p>
- 支持生成以下分辨率的图片:768:768(1:1)、768:1024(3:4)、1024:768(4:3)、1024:1024(1:1)、720:1280(9:16)、1280:720(16:9)、768:1280(3:5)、1280:768(5:3)、1080:1920(9:16)、1920:1080(16:9),不传默认使用768:768。
*/
private String resolution = "768:768";/**
- 返回图像方式(base64 或 url) ,二选一,默认为 base64。url 有效期为1小时。
*/
private String rspImgType = "url";
}
在 AIGCUtils 当中 新增文本生成图片静态textToImage
方法
这样子即可 styles 我们只需要一个则直接 new 一个数组搞进去,水印的话大佬感兴趣也可以自己搞进去一样的操作小问题单元测试我们已经编写过!!!
/**
- 腾讯云 AIGC 文本生成图片
<a href="https://console.cloud.tencent.com/api/explorer?Product=aiart&Version=2022-12-29&Action=TextToImage">...</a>
*/
public static TextToImageResponse textToImage(TextToImage req) {
try {
final TextToImageRequest request = new TextToImageRequest();
// 文本描述
request.setPrompt(req.getPrompt());
// 反向文本描述
request.setNegativePrompt(req.getNegativePrompt());
// 图片风格
request.setStyles(new String[]{req.getStyles()});
// 分辨率
final ResultConfig config = new ResultConfig();
config.setResolution(req.getResolution());
request.setResultConfig(config);
// 返回图像方式(base64 或 url) ,二选一,默认为 base64。url 有效期为1小时。
request.setRspImgType("url");
return getAiartClient().TextToImage(request);
} catch (TencentCloudSDKException e) {
log.error("腾讯云 AIGC 文本生成图片异常", e);
throw new RuntimeException("腾讯云 AIGC 文本生成图片异常");
}
}
编写文本生成图片接口
在 com.yby6.tencentaigc
下面新增文件夹 controller
在新增 AIGCController
控制层
package com.yby6.tencentaigc.controller;
import com.tencentcloudapi.aiart.v20221229.models.TextToImageResponse;
import com.yby6.tencentaigc.domain.TextToImage;
import com.yby6.tencentaigc.resp.R;
import com.yby6.tencentaigc.utils.AIGCUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/**
人工智能内容生成接口
@author Yang Shuai
Create By 2023/12/10
*/
@RestController
@RequestMapping("/aigc")
public class AIGCController {/**
- 文本转图像请求
- @return {@link R}<{@link TextToImageResponse}>
*/
@PostMapping("text")
public R<TextToImageResponse> text(@RequestBody TextToImage request) {
return R.ok(AIGCUtils.textToImage(request));
}
}
好啦到这里就大功告成!!! 累死了 喜欢本篇文章麻烦点个赞谢谢!!! ❤️
接口编写完毕我们进行测试文本生成图片接口
效果图
五、前端搭建
描述
我们在上面已经将后端服务完完全全的搞完了(还差图生图也很简单感兴趣的同学可以自己发挥前端页面我是已经给大家准备好了!!!贴心吧)
废话不多说直接开始我们的前端搭建
我这里提供两种方式一个是直接下我的脚手架进行页面开发一个是你自己创建项目开发
注意⚠️: 文章基于脚手架开发(主要是一些配置而已) 页面还是一步步来玩
项目脚手架(推荐) 点击下载
搭建 Vite 项目
使用NPM:
npm create vite@latest
使用 Yarn:
yarn create vite
使用 PNPM:
pnpm create vite
我这里就使用NPM来安装
注意: 第一次可能会弹出是否继续 直接Y
即可
- 执行
npm create vite@latest
- 自定义目录名称img
- 选择模板
vite
img - 选择语言
javaScript
- 进入新建的目录
npm install && npm run dev
如果很慢执行 npm install cnpm -g --registry=https://registry.npm.taobao.org
运行成功后会出现如下界面
启动项目
可以看到我已经写好了菜单栏了,主要是这个简单而且就这个东西没必要你们去写了因此我还非常非常非常非常简单的录制了个讲解视频可能说的也不咋好将就看吧第一次录制自己讲解的....哈哈哈
六、装修文生文页面
介绍一下我们的老朋友了 ElementPlus 组件库, 我们使用该组件库进行页面的渲染和搭建.
需求
可以看到上面的布局是非常简单的,左右布局一下,这时候我们就可以使用弹性布局了 display flex 如果不清楚的小伙伴可以百度花个五六分钟看看非常简单,
我这里就简单的说一下
- 弹性容器(Flex Container):使用 display: flex 或 display: inline-flex 属性的元素被视为弹性容器。在这个容器内的子元素将遵循弹性布局。
- 弹性项目(Flex Items):弹性容器内的直接子元素自动成为弹性项目,这些项目可以使用各种弹性属性进行布局调整。
- 主轴(Main Axis)和交叉轴(Cross Axis):弹性布局中有两个主要轴线。主轴是沿着弹性项目排列的方向,而交叉轴垂直于主轴。
- 弹性属性:如 flex-grow, flex-shrink, 和 flex-basis 这些属性用于控制弹性项目的伸缩行为。
- 对齐属性:如 justify-content, align-items, 和 align-self,这些属性控制如何在主轴或交叉轴上对齐弹性项目。
那么如何布局左右两边呢?并且控制某一边的宽度另一边就自适应,下面我们开始吧!!!
装修
认识Container 布局容器,打开 ElementPlus 找到容器布局,我们可以借用这个来实现左右的完美布局,可以看看参数
地址: https://element-plus.gitee.io/zh-CN/component/container.html#container-slots
这里我们就可以控制侧边栏的宽度了然后另外的一半就自适应了我们可以试试看
新增代码侧边栏和显示 我们使用一个大的 div 来包裹起来方便管理标签
<div class="main">
<el-aside class="div-left">
我是侧边栏
</el-aside>
<el-main class="div-main">
我是显示图片
</el-main>
</div>
查看新增的代码效果,为了看得清楚我给两个元素加了背景颜色
怎是这样子? 不是两边给我布局吗,因为我们定义了一个 main div 容器组件库的样式无法找到我们去除试试看?
这样子就好看多了呀,一下子就实现了我们的效果,但是我们不进行使用它来搭建项目我们自己写样式!!!! 😎
如果不行自己写就可以使用组件库的给侧边栏设置宽度就完事了
装修侧边栏
在上面我们用了组件库来实现左右布局,接下来我们自己是实现,还是一样定义一个 div mian 来包裹左右布局标签代码
<div class="main">
<!-- 左边开始 -->
<div class="div-left">
我是侧边栏
</div>
<!-- 右边开始 -->
<div class="div-main">
我是显示图片
</div>
</div>
我滴妈打回原形了好难看呀,继续装修!!!!!!!!!!!!!
我想要 main 里面的 两个子元素左右布局,使用 弹性布局 display flex
.main {
display: flex;
width: 100%;
height: 100%;
font-size: 14px;
}
给父元素设置开启弹性布局,让其子元素可以使用 flex 进行控制元素占用的大小
.main {
display: flex;
width: 100%;
height: 100%;
font-size: 14px;
}.div-left {
background-color: red;
color: white;
flex: 0 0 364px;
flex-direction: column; /* 将侧边栏内容以垂直方向排列 */
}
.div-main {
background-color: blue;
color: white;
flex: 1 1; /* 占据剩余的所有空间 */
padding: 80px;
box-sizing: border-box;
}
给子元素设置规则 flex 让其占用多少位置
- flex: 0 0 364px;:
- 这一行设置了弹性项目的伸缩性(flexibility)
- 第一个值 0 表示弹性项目不会在自由空间中增长,它将占据其指定的空间
- 第二个值 0 表示弹性项目不会在自由空间中收缩,即不会缩小以适应容器
- 第三个值 364px 表示弹性项目的初始大小,它将占据 364 像素的空间
- flex-direction: column;:
- 这一行设置了弹性容器的主轴方向
- column 值表示主轴方向为垂直方向,即弹性项目将垂直排列在弹性容器内
查看效果
嗯嗯~ 真舒服,和上面的一样完美实现!
编写风格选择
可以看到这里的样式很简单,但是和下面一样所以我们就写在一个 class 里面到时候其他的直接用
<!-- 左边开始 -->
<div class="div-left">
<!-- 内边距 16 -->
<div class="padding16">
<!-- 小标题 -->
<div class="line-item">
<label for="">风格选择</label></div>
</div>
</div>
我们定义了一个 padding16 样式 里面的样式 为 内边距 16px 像素
小标题我们给他定义了一个外边句。上边距 5px 像素 下边距 5px 像素 作用就是 和其他的元素隔开一点点搞点空间感不那么拥挤!!!!
我们还可以看到标题旁边有个 红色的花这个一般叫做必须输入、选择等校验的操作
给小标题里面的 label 搞个伪元素
:after 是一个CSS伪元素(pseudo-element),它用于向元素的内容之后插入生成的内容。伪元素是用于样式化元素的虚拟元素
示例
p::after {
content: "我是内容";
}
查看效果
好那么基本的完成了我们复制几份看看,可以看到我们的小标题就完成啦!!!
ok 我们加快速度冲冲冲!!!
风格选择
可以看到他是一个正方形,那么正方形里面有一个长方形此时怎么说如果学过 HTML5+CSS3 的同学就清晰的知道这种就是 定位元素(position)
的功劳了,有个专业名词叫做 子绝父相
,那么它的作用就是子元素以父元素的边界进行布局,
在文档流当中子元素是默认跟着父元素滴,也就是说我父亲有多大父亲在页面的哪个位置儿子就默认在父亲元素的里面.
我给大家演示一下, 可以看到我在这里只是定义了两个容器并且给了不同的宽高和背景颜色,可以看到子元素没设置任何影响它的样式,好那么我想要把子元素定位到父亲的尾部如何操作?
前面我们讲了定位元素(position)
,没说它是啥那么我们看看它的几个参数作用
- 相对定位(relative positioning):使用 position: relative; 属性,可以将元素相对于其正常位置进行定位。通过设置 top、right、bottom 和 left 属性,可以在水平和垂直方向上移动元素。相对定位不会影响其他元素的位置,
可以看到我们对第一个子元素设置了相对定位,我们直接 向下移动了 20 像素,相对它自己所以不会影响其它的元素!!!!
- 绝对定位(absolute positioning):使用 position: absolute; 属性,可以将元素相对于其最近的定位(position 属性非 static 的)祖先元素进行定位。通过设置 top、right、bottom 和 left 属性,可以在水平和垂直方向上移动元素。绝对定位会使元素脱离文档流,不影响其他元素的位置。
可以看到我们给第一个子元素设置了绝对定位她也就脱离文档流了所以直接覆盖在任意元素上面都 okkk 的,如果你没有为他的父设置相对那么它默认就是整个浏览器的参照物的视角,那么我们看看设置一下父元素的相对定位是什么效果?
可以看到我们设置父相对那么子元素就直接脱离文档流,第二个儿子元素默认就往上挤了,子元素也设置了 left 50px 相右边移动五十像素,下边移动 20 像素!!!
接下来我给大家演示看看使用开发者工具调试看看效果,注意看我鼠标 F12 打开点击鼠标图标在点击对应元素即可对其样式更改和操作.
<div style="background-color:lightpink;height: 600px;width: 1200px;margin: 0 auto;">
<div style="width: 300px;height: 300px;margin: 0 auto;background-color: red;">
<span>我是父亲</span><div style="width: 100px;height: 100px;background-color: #fff;"> 我是儿子元素 </div> <div style="width: 100px;height: 100px;background-color: yellow;"> 我是儿子元素22222222222222 </div>
</div>
</div>
还有固定定位 fixed 、粘性定位sticky、感兴趣的可以去度一下学习很简单!!! 我这里就说说 相对和绝对定位
好啦讲了这么多,我们回过头来看,描述搞定了看看这么多正方形我难道要一个个复制啊? nonono!!
可以看到上面的 需求图片风格列表
相对于一个列表的形似我们就可以使用 Vue 当中的 v-for 指令对其进行循环操作
我们先把一个正方形的样式搞出来后面的就都是循环复制复制复制一份份就行
我们定义一个风格选择组的容器,里面有图片、和描述 并且我钉死了正方形的大小为 94px 像素
<div class="item-group">
<!-- for循环 -->
<div class="item-img">
<div style="width: 94px;height: 94px;">
<!-- 模拟图片 -->
<div style="width: 100%;height: 100%;background-color: red;"></div>
<!-- 图片描述 -->
<div class="item-img-remark">
我是描述
</div>
</div>
</div>
</div>
看看效果,我们的形状出来了 接下来就是装修样式了,我给大家写好了并且注释也打满了非常的易懂,如果不会评论区留言我手把手好吧!
.item-group {
/* 设置右外边距,用于消除包含元素的外部间隙 */
margin-right: -16px;/* 设置最大高度,超过高度的内容将显示滚动条 */
max-height: 406px;/* 隐藏水平方向的溢出内容,显示垂直方向的滚动条 */
overflow-x: hidden;
overflow-y: auto;/* 设置顶部外边距为10像素 */
margin-top: 10px;/* 使用弹性布局,项目将自动包装并水平排列 */
display: flex;
flex-wrap: wrap;/* 将项目在主轴上左对齐 */
justify-content: flex-start;
}/* .item-img 类的样式 /
.item-img {
/ 设置相对定位,用于后续绝对定位元素的参考 */
position: relative;/* 设置右外边距,用于在项目之间创建间距 */
margin-right: 9px;/* 设置底部外边距,用于在项目之间创建垂直间距 */
margin-bottom: 10px;/* 设置光标样式为手型,表示可以点击 */
cursor: pointer;/* 设置边框,初始状态下是透明的 */
border: 3px solid transparent;/* 设置盒子模型,确保边框宽度不会增加元素的尺寸 */
box-sizing: border-box;
}/* .item-img 类内的 img 元素的样式 /
.item-img img {
/ 设置图像的宽度和高度 */
width: 94px;
height: 94px;
}/* .item-img-remark 类的样式 /
.item-img-remark {
/ 设置绝对定位,位于 .item-img 内的底部 */
position: absolute;
bottom: 0;
left: 0;
right: 0;/* 设置高度和行高,用于容纳文本内容 */
height: 30px;
line-height: 30px;/* 设置文本居中对齐 */
text-align: center;/* 设置背景颜色,半透明黑色 */
background-color: rgba(0, 0, 0, 0.5);/* 设置文本颜色为白色 */
color: white;
/* 设置字体大小 */
font-size: 12px;
}
查看效果,非常的 nice
循环风格选择列表
接下来我们操作 vue 来实现循环
定义表单对接前面我们写的 api 的接口参数
// 表单
let dataForm = ref({
prompt: "",
negativePrompt: "",
styles: "",
resolution: "768:768",
url: ""
})
实现定义一个模拟去请求数据过来,我辛辛苦苦把腾讯云智能图像平台的图片拉下来的呜呜呜,麻烦点个赞吧我累死了!!!!!
// 风格类型集合
let itemList = ref([])
// 获取风格类型集合
const styleList = () => {const imgList = [
// { category: '不限定风格', subCategory: '不限定风格', code: '000', url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"},
{
category: '游戏动漫类',
subCategory: '日系动漫',
code: '201',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '游戏动漫类',
subCategory: '怪兽风格',
code: '202',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '游戏动漫类',
subCategory: '唯美古风',
code: '203',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '游戏动漫类',
subCategory: '复古动漫',
code: '204',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '游戏动漫类',
subCategory: '游戏卡通手绘',
code: '301',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '专业写实类',
subCategory: '通用写实风格',
code: '401',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '水墨画',
code: '101',
url: "https://foruda.gitee.com/images/1702228291422424441/ee2bfb5a_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '概念艺术',
code: '102',
url: "https://foruda.gitee.com/images/1702228731588263900/0171e53c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '油画1',
code: '103',
url: "https://foruda.gitee.com/images/1702228390334013230/63e314e5_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '油画2(梵高)',
code: '118',
url: "https://foruda.gitee.com/images/1702228390334013230/63e314e5_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '水彩画',
code: '104',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '像素画',
code: '105',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '厚涂风格',
code: '106',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '插图',
code: '107',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '剪纸风格',
code: '108',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '印象派1(莫奈)',
code: '109',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '印象派2',
code: '119',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '2.5D',
code: '110',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '古典肖像画',
code: '111',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '黑白素描画',
code: '112',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '赛博朋克',
code: '113',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '科幻风格',
code: '114',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '暗黑风格',
code: '115',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '3D',
code: '116',
url: "https://foruda.gitee.com/images/1702228390334013230/63e314e5_5151444.png"
},
{
category: '艺术绘画类',
subCategory: '蒸汽波',
code: '117',
url: "https://foruda.gitee.com/images/1702228211279649218/77eb500c_5151444.png"
}]
// 将请求过来的集合数据放进去
itemList.value = imgList
// 初始化选中第一个
dataForm.value.styles = itemList.value[0].code
}
编写完毕后我们在生命周期当中调用模拟打开页面请求
修改我们的 html 标签,手动操作一下吧
<div class="item-group">
<!-- for循环 -->
<div class="item-img" v-for="item in itemList" :key="item.code">
<div style="width: 94px;height: 94px;">
<!-- 图片 -->
<img :src="item.url" alt="">
<!-- 图片描述 -->
<div class="item-img-remark">
{{ item.subCategory }}
</div>
</div>
</div>
</div>
查看效果,直接完美实现!! 是不是很简单 弹性布局 nice 的很
差不多搞定啦,还差一个点击效果! 和上面的菜单栏一样两个按钮的背景颜色切换!!一样的思路
这里停留 3 秒.............................想到咋实现了吗.?
我们定义一个点击事件里面传递当前点击元素的唯一标识,然后在定义一个变量存储这个标识
在使用 vue 当中的 动态样式绑定我们需要指定变化的样式就行啦~ 来吧我们操作操作!!!!!
我们给点击事件添加一个日志打印看看效果
这样子我们就可以知道当前点击的是谁接下来我们进行添加动态样式,给点击的元素加个边框!!!
定义一个边框元素,然后通过动态样式进行动态的改变样式具体解释如下:
我们有一个名为 item-img 的 CSS 类和一个名为 activeItem 的 CSS 类。我们希望根据某个条件为 HTML 元素添加或删除这两个类。
条件如下:
- 'item-img': true:无论什么情况,item-img 类总是会被添加到 HTML 元素上。
- 'activeItem': dataForm.styles === item.code:只有当 dataForm.styles 的值等于 item.code 时,activeItem 类才会被添加到 HTML 元素上。
- important 作用于强行覆盖它原有身上一样的样式 就是 原先你有边框样式,我要强制改成 我的样式!!!
看到这里是不是觉得 so easy to happy 和切菜一样 简单
ok 接下来我们看看效果, 可以看到动态图片已经完美的实现啦
接下来就是基础的表单元素啦,很简单我就不带着大家做了,希望有能力的可以自己动手玩玩,我就不那么详细的一步步说了,我就一步步的截图+代码实现.
打开组件库我们搜索表单组件 https://element-plus.gitee.io/zh-CN/component/menu.html
描述词
可以看到需求图片上面是一个 文本域的组件那么直接在文档当中找到即可
这里我就直接文档找到了 输入框 类型是 textarea 文本域
使用 prompt 来暂存我们的信息
- v-model="dataForm.prompt":这是一个 Vue.js 指令,用于在输入框和组件之间创建双向数据绑定当用户在输入框中输入文本时,dataForm.prompt 的值将自动更新
- placeholder="请输入您想要的画面或者使用下面的推荐词":这个属性为输入框提供了一个占位符文本
- type="textarea":这个属性将输入框的类型设置为多行文本输入框(textarea)而不是默认的单行输入框
- :rows="5":这个属性设置了文本输入框的行数,就跟着图片来它多大我们就调试为多大
- show-word-limit:这个属性启用了输入框下方的字数限制显示
- maxlength="250":这个属性设置了输入框的最大字符数
查看效果
反向词
顾名思义就是希望生成的画面当中不希望出现的元素,那么它也是文本域 so easy 啦
使用 negativePrompt 来暂存我们输入的信息
查看效果
图片尺寸
选择要生成的图片大小格式,是一个下拉框
大家手动操作一下!!!
数据我也给大家准备好了,看到这里还不点个赞嘛死鬼!
// 分辨率集合
let resolutionList = [
{label: "768:768(1:1)", value: "768:768"},
{label: "768:1024(3:4)", value: "768:1024"},
{label: "1024:768(4:3)", value: "1024:768"},
{label: "1024:1024(1:1)", value: "1024:1024"},
{label: "720:1280(9:16)", value: "720:1280"},
{label: "1280:720(16:9)", value: "1280:720"},
{label: "768:1280(3:5)", value: "768:1280"},
{label: "1280:768(5:3)", value: "1280:768"},
{label: "1080:1920(9:16)", value: "1080:1920"},
{label: "1920:1080(16:9)", value: "1920:1080"}
]
<!-- 小标题 -->
<div class="line-item">
<label for="">风格选择</label>
<div class="item-group">
<!-- for循环 -->
<div
:class="{
'item-img': true,
'activeItem': dataForm.styles === item.code
}"v-for="item in itemList" @click="selectStyle(item)" :key="item.code"> <div style="width: 94px;height: 94px;"> <!-- 图片 --> <img :src="item.url" alt=""> <!-- 图片描述 --> <div class="item-img-remark"> {{ item.subCategory }} </div> </div> </div> </div> </div> <!-- 小标题 --> <div class="line-item"> <label for="">描述词</label> <el-input v-model="dataForm.prompt" placeholder="请输入您想要的画面或者使用下面的推荐词" type="textarea" :rows="5" show-word-limit maxlength="250" /> </div> <!-- 小标题 --> <div class="line-item"> <span>反向词</span> <el-input v-model="dataForm.negativePrompt" placeholder="请输入画面中不希望出现的元素,例如: 模糊" type="textarea" :rows="5" show-word-limit maxlength="250"/> </div> <!-- 小标题 --> <div class="line-item"> <label for="">图片尺寸</label> <el-select style="width: 100%;" v-model="dataForm.resolution" class="m-2" placeholder="请选择希望生成的图片分辨率" size="large"> <el-option v-for="item in resolutionList" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </div></code></pre></div></div><p>查看下拉框效果</p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:79.09%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722969578853446565.png" /></div><div class="figure-desc">img</div></div></div></figure><h4 id="26ihi" name="%E6%95%B4%E4%BD%93%E6%9F%A5%E7%9C%8B%E6%95%88%E6%9E%9C">整体查看效果</h4><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:79.09%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722969579345103553.png" /></div><div class="figure-desc">img</div></div></div></figure><h4 id="2bh4d" name="%E6%8F%90%E4%BA%A4%E6%8C%89%E9%92%AE%E7%94%9F%E6%88%90%E7%94%BB%E4%BD%9C">提交按钮生成画作</h4><p>这就是一个按钮将上面全部需要输入的信息发起请求到后端进行处理即可</p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:79.09%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722969579820895225.png" /></div><div class="figure-desc">img</div></div></div></figure><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:79.09%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722969580153075471.png" /></div><div class="figure-desc">img</div></div></div></figure><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>html</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-html"><code class="language-html" style="margin-left:0"><div style="margin-top: 5px;margin-bottom: 5px;">
<el-button type="primary" style="width: 100%;margin-top: 15px;height: 35px">
生成画作
</el-button>
</div>
查看最终效果
修改背景颜色
.div-left {
// background-color: #cccccc; 删除
flex: 0 0 364px;
flex-direction: column; /* 将侧边栏内容以垂直方向排列 */
}
.div-main {
background-color: #eff8ff; // 灰色好看
color: white;
flex: 1 1; /* 占据剩余的所有空间 */
padding: 80px;
box-sizing: border-box;
}
看清楚嗷 侧边栏去掉,图片显示区域颜色改改为 background-color: #eff8ff;
查看效果 完美~
装修中间区域
查看效果图, 其实就是外层套了个容器里面有一个 icon 图标、然后在嵌套一个容器用来装文字描述信息
<div class="div-main-about">
<div class="div-main-about__object"><i class="div-main-about__icon"></i></div>
<div class="div-main-about__main">
<div class="div-main-about__title">在左侧输入描述词开始创作吧</div>
</div>
</div>
上色
我们看看官方是咋搞的 UI , 一个容器包裹并且给了点阴影样式,里面就是图标和描述,直接开干
我们为 .div-main-about
容器设置一下弹性布局
- 设置
box-sizing
属性为border-box
,这样元素的宽度和高度将包括内边距和边框,但不包括外边距。 - 将
display
属性设置为flex
,使元素成为弹性容器。 - 将
flex-direction
属性设置为column
,使子元素按照垂直方向排列。 - 将
align-items
属性设置为center
,使子元素在水平方向上居中对齐。 - 将
justify-content
属性设置为center
,使子元素在垂直方向上居中对齐。 - 设置
background-color
属性为hsla(0, 0%, 100%, 0.6)
,使元素的背景颜色为半透明的白色。 - 设置
border-radius
属性为3px
,使元素的边框圆角半径为3像素。 - 设置
flex
属性为1 1 auto
,使元素在弹性容器中占据可用空间。 - 设置
height
属性为100%
,使元素的高度为100%。 - 设置
min-height
属性为0
,使元素的最小高度为0。 - 设置
min-width
属性为0
,使元素的最小宽度为0。 - 设置
width
属性为100%
,使元素的宽度为100%。
我们在设置一下图标
.div-main-about__icon.icon-upload
子元素,设置background-image
属性为指定的URL,使该子元素的背景图像为指定的图像- 对
.div-main-about__icon
子元素,设置background-position
、background-repeat
、background-size
、display
、height
、margin
和width
属性,以定义子元素的背景图像样式、显示方式、大小和边距
设置标题
- 对
.div-main-about__title
子元素,设置color
、font-size
、font-weight
、line-height
、text-align
和margin-top
属性,以定义子元素的颜色、字体大小、字体粗细、行高、对齐方式和上边距。
七、图生图页面
还差图生图的页面其实和文生文一摸一样我们可以直接复制一份文生文的代码
那么就有一个问题,css 好像都一样的诶?????
那么我们就可提出来,我给大家搞了个全局样式只需要把样式移动就来就行
删除两个页面里面的 CSS 样式即可
查看效果
我滴妈好难看啊? 为什么? 还记得文档流嘛,我们前面都是多个块级元素慢慢的顶满整个高度的
那么如何解决这个问题,我想让他直接顶满整个高度, 那么就可以使用 VM
要使HTML中的侧边栏高度始终占据屏幕的最高,你可以使用CSS的 height: 100vh
属性
这将使侧边栏的高度始终等于视口高度(即屏幕高度)
最终效果
我这里给大家提供一个图片上传的组件我已经实现完毕,大家可以参考,下面代码是关键的代码片段
const url = ref(undefined)
const {proxy} = getCurrentInstance()
const httpRequest = (data) => {
let rd = new FileReader() // 创建文件读取对象
let file = data.file
rd.readAsDataURL(file) // 文件读取装换为base64类型
rd.onloadend = (e) => {
// this指向当前方法onloadend的作用域, this.result就是文件的base64, 这里可自由处理
url.value = e.currentTarget.result
proxy.$emit('successBase64', e.currentTarget.result)
}
}
终于写完啦! 我们的样式到此就告一段落啦
接下来我们就是进行对接口了调用我们前面写完的后端服务接口实现文生图!!
八、对接
首先感谢捧场!!!!看到这里已经不容易了我也写了几天时间感谢大佬们的捧场!!事不宜迟我们直接继续完成最后阶段的学习!!!
在上面我们已经全部完成了前后端的应用服务的搭建和编写接下来我们进行将我们的前端进行对接后端接口实现图片生成的完整步骤流程!!!
在前面的时候我已经实现了文生图的接口,还有图生图的接口没有实现实际上我也已经写好了只是想让小伙伴自己玩玩提升学习效率因为它和文生图是一样的操作方式非常的简单!!!
图生图的工具方法,动手能力强的可以和文生图一样进行操作,我们前端已经搭建完毕只需要后端编写一下接口即可!!!大佬们冲冲冲!
前端 API 编写
在前面编写前端页面的时候我给大家整了个脚手架并且搭配了视频讲解说了一下 API 的作用
现在是给没使用脚手架的同学讲解一下那么 我们前往前端项目文件夹 在 src/api
下面创建 apigc.js 文件
// yangbuyi Copyright (c) https://yby6.com 2023.
import request from '@/utils/request';
/**
- 文字转图片
- @param data
@returns {*}
*/
export function textToImage(data) {
return request({
url: '/aigc/textToImage',
method: 'post',
data
});
}
提交请求
我们给按钮组件新增一个点击事件 并且 新增一个 点击后禁止再次点击防止用户重复疯狂的无效点击
在定义一个获取当前实例用于调用我们挂载的全局弹出组件实现前端提示效果
<el-button
@click="submit" :disabled="disabledFlag"
type="primary" style="width: 100%;margin-top: 15px;height: 35px">
生成画作
</el-button>
// 获取当前实例
const {proxy} = getCurrentInstance();
// 提交按钮
let disabledFlag = ref(false);
基本的变量完成之后我们来编写请求函数,可以看到我定义了一个函数 submit 并且设置了点击后禁止再次点击还判断了后端接口必须填写的一些参数校验,最终进行发送请求
导入我们的 API 请求方法
测试
打开后端服务,打开前端服务
选中你想要的风格,输入好你想要的图片描述直接提交
可以看到我们点击后按钮意见禁止了并且成功的发起了请求返回到我们的控制台当中
描述词: 小女孩,在河边,翅膀,摸着小猫,星空,森林
反向描述: 模糊,扭曲
接下来我们把请求返回的图片在我们的页面当中展示出来!!!!
数据结构
{
"code": 200,
"msg": "操作成功",
"data": {
"resultImage": "https://aiart-1258344699.cos.ap-guangzhou.myqcloud.com/text_to_img/176634a2-ed41-4157-ba03-05a84e8fec8f-1256625019.jpg?q-sign-algorithm=sha1&q-ak=AKIDpRovliU1IJ5ctufBSVIq8AwTlnZ5MN8d&q-sign-time=1702789054%3B1702792654&q-key-time=1702789054%3B1702792654&q-header-list=host&q-url-param-list=&q-signature=75ab80ece335025c25fc480c",
"requestId": "xxxxxxxx"
}
}
可以看到resultImage 才是我们需要的所以我们直接提取它即可
判断返回过来的是否正确,正确则往表单对象当中新增一个 url 的属性并且赋值生成的图片地址
结束后我们 2 秒后在进行按钮的释放.
前端呢我们就需要对应的修改了判断表单对象当中的 URL 是否存在参数存在则显示图片不存在则显示图标
文字提醒也是如此
展示效果
九、生成提示音
我这里写了一篇非常非常非常非常详细的文章《重生之我在这个世界的文本转音频API工程师的故事
这里看你喜欢用哪个就用哪个我这里用腾讯云的 下面我就开始从零到壹的教你们咋玩
十、腾讯云文本转音频Text To Speech,TTS
开通
首先前往腾讯云语音合成开通服务 可以免费领取三个月的资源用不完的那种哦
语音合成服务
然后我们来到合成音频来调试合成音频的语音包看看那个适合你,我这里就选了小荷了听的感觉不错,还可以选择情感风格我滴妈.点击立刻合成
可以看到已经消耗了九个字符没关系我们有八百万就是豪气
文档
点击合成音频看看右下角点击语音合成
接口描述
接口请求域名: tts.tencentcloudapi.com
腾讯云语音合成技术(TTS)可以将任意文本转化为语音,实现让机器和应用张口说话。
腾讯TTS技术可以应用到很多场景,比如,移动APP语音播报新闻;智能设备语音提醒;依靠网上现有节目或少量录音,快速合成明星语音,降低邀约成本;支持车载导航语音合成的个性化语音播报。
接口要求
实际上也就两个必填 Text、SessionId 以下是我觉得符合我的业务的必要参数
具体的参数前往文档查看
参数名称 | 必选 | 类型 | 描述 |
---|---|---|---|
Text | 是 | String | 合成语音的源文本,按UTF-8编码统一计算。 中文最大支持150个汉字(全角标点符号算一个汉字);英文最大支持500个字母(半角标点符号算一个字母)。 示例值:你好 |
SessionId | 是 | String | 一次请求对应一个SessionId,会原样返回,建议传入类似于uuid的字符串防止重复。 示例值:session-1234 |
VoiceType | 否 | Integer | 音色 ID,包括标准音色与精品音色,精品音色拟真度更高,价格不同于标准音色,请参见购买指南。完整的音色 ID 列表请参见音色列表。 示例值:0 |
PrimaryLanguage | 否 | Integer | 主语言类型:1-中文(默认)2-英文3-日文 示例值:1 |
Codec | 否 | String | 返回音频格式,可取值:wav(默认),mp3,pcm 示例值:wav |
EmotionCategory | 否 | String | 控制合成音频的情感,仅支持多情感音色使用。取值: neutral(中性)、sad(悲伤)、happy(高兴)、angry(生气)、fear(恐惧)、news(新闻)、story(故事)、radio(广播)、poetry(诗歌)、call(客服)、撒娇(sajiao)、厌恶(disgusted)、震惊(amaze)、平静(peaceful)、兴奋(exciting)、傲娇(aojiao)、解说(jieshuo) |
输出参数
参数名称 | 类型 | 描述 |
---|---|---|
Audio | String | base64编码的wav/mp3音频数据 示例值:UklGRlR/AABXQVZFZm10IBAAAAABAAEAgD4AAAB9AAACABAAZGF0YSx9AAD+ |
SessionId | String | 一次请求对应一个SessionId 示例值:session-1234 |
Subtitles | Array of Subtitle | 时间戳信息,若未开启时间戳,则返回空数组。 |
RequestId | String | 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 |
接口调试
只要有 API Explorer 那就是闭着眼玩,腾讯都给你实现好了闭眼冲
在前面我们说到这六个我就给这六个参数设置参数其它的你们自己看看需要什么搞什么
- Text
- SessionId
- VoiceType
- PrimaryLanguage
- Codec
- EmotionCategory
点击每个参数的梅花可以看到对应解释
PrimaryLanguage
设置合成语音语言默认为中文有需要的就自己改改我就默认了
VoiceType
点击音色列表选择你觉得好听的音色我这里就选择 爱小荷 301032
如果不知道是什么样子去 合成音频菜单
调试一下看看你喜欢哪个口味的
Codec
设置返回的格式我这里就 mp3 格式
EmotionCategory
设置情绪 控制合成音频的情感,仅支持多情感音色使用。取值: neutral(中性)、sad(悲伤)、happy(高兴)、angry(生气)、fear(恐惧)、news(新闻)、story(故事)、radio(广播)、poetry(诗歌)、call(客服)、撒娇(sajiao)、厌恶(disgusted)、震惊(amaze)、平静(peaceful)、兴奋(exciting)、傲娇(aojiao)、解说(jieshuo)
选择对应的调试代码
可以直接下载工程,我这里就直接复制工程了
如果你是复制的工程片段代码那么就要手动引入 maven 坐标依赖 SDK 了
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-common</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-tts</artifactId>
<version>LATEST</version>
</dependency>
如果不知道密钥的
密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
前面忘记选择资源地域了 Region 我们直接填写里你自己城市进的我这里就上海了
ap-shanghai
测试
可以看到返回的是一个 base64
复制一波拼接 data:audio/mp3;base64,
的前缀 下面是我生成的听听看哈哈哈
data:audio/mp3;base64,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
直接访问完事
前端我就不搞咯,一样的 so easy 切菜一样
十一、前往 AIGC 项目编写工具类
在 com.yby6.tencentaigc.utils
目录下新增 TencentUtils
工具类
新增一个方法文本转语音方法
/**
- 文本转语音
- @param request 参数
- @return {@link String}
*/
public static String textToVoice(TextToVoiceRequest request) {
return null;
}
改造 textToVoice
把前面我们调试的代码复制到 textToVoice 当中
修改成下面的图片,使用 TextToVoiceRequest 来接收外界传递的参数即可
package com.yby6.tencentaigc.utils;
import cn.hutool.core.bean.BeanUtil;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.tts.v20190823.TtsClient;
import com.tencentcloudapi.tts.v20190823.models.TextToVoiceRequest;
import com.tencentcloudapi.tts.v20190823.models.TextToVoiceResponse;
import com.yby6.tencentaigc.config.TencentConfig;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;/**
腾讯云语音合成
@author Yang Shuai
Create By 2023/12/21
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TencentUtils {
private static TencentUtils tencentUtils;
private final TencentConfig tencentConfig;/**
- 要在静态方法里面调用IOC容器的bean对象
- PostConstruct 在构造函数执行之后执行。
- 可以方便的把注入的bean对象给到静态属性
- 源码: AutowiredAnnotationBeanPostProcessor 的 buildAutowiringMetadata 函数
- isStatic 绕过了静态不进行注入
*/
@PostConstruct
public void postConstruct() {
tencentUtils = this;
}/**
文本转语音
@param request 参数
@return {@link String}
*/
public static String textToVoice(TextToVoiceRequest request) {
Credential cred = new Credential(tencentUtils.tencentConfig.getSecretId(), tencentUtils.tencentConfig.getSecretKey());
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("tts.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
TtsClient client = new TtsClient(cred, "ap-shanghai", clientProfile);
TextToVoiceRequest req = new TextToVoiceRequest();
BeanUtil.copyProperties(request, req);
// 返回的resp是一个TextToVoiceResponse的实例,与请求对象对应
TextToVoiceResponse resp = null;
try {
resp = client.TextToVoice(req);
log.info("rep:{}", resp.getRequestId());
} catch (TencentCloudSDKException e) {
log.error("【腾讯云语音合成接口调用异常】", e);
throw new RuntimeException(e);
}return "data:audio/mp3;base64," + resp.getAudio();
}
}
测试
在 com.yby6.tencentaigc.controller
下面创建 AudioController 路由类
// yangbuyi Copyright (c) https://yby6.com 2023.
package com.yby6.tencentaigc.controller;
import cn.hutool.json.JSONUtil;
import com.tencentcloudapi.tts.v20190823.models.TextToVoiceRequest;
import com.yby6.tencentaigc.resp.R;
import com.yby6.tencentaigc.utils.XunFeiUtil;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Base64;/**
语音合成
@author Yang Shuai
Create By 2023/12/21
*/
@RequestMapping("/speechSynthesis")
@RestController
@RequiredArgsConstructor
public class AudioController {private static final Logger log = LoggerFactory.getLogger(AudioController.class);
@PostMapping("tencentTextToVoice")
public R<String> tencentTextToVoice(@RequestBody TextToVoiceRequest request) {return R.ok(JSONUtil.toJsonStr(request));
}
}
打开请求工具任何都可以下面是参数可以自己修改
{
"text": "你好呀 杨不易",
"sessionId": "123156161561561",
"voiceType": 301032,
"codec": "mp3",
"emotionCategory": "happy"
}
调通完毕,我们直接调用工具类试试看修改请求方法
再次请求(记得重新启动服务)
可以看到我们请求成功了接下来就是前端对接了
前端对接语音合成
如果使用的讯飞那么返回的是数据流需要 URL.createObjectURL(response)
通过这个API让语音数据转为成一个url地址
腾讯云语音合成是 base64 我们可以直接把 base64 放进去直接播放.操作操作
在 AIGCUI/src/api/audio.js
新增audio.js
接口
// yangbuyi Copyright (c) https://yby6.com 2023.
import request from '@/utils/request';
/**
-
腾讯语音合成
-
@param data
@returns {*}
/
export function tencentTextToVoice(data) {
return request({
url: '/speechSynthesis/tencentTextToVoice',
method: 'post',
data
});
}
前往 index 页面新增音频播放器 给了个唯一 ID
<audio id="indexAudioId" autoplay hidden="hidden"></audio>
新增函数 getTencentTextToVoice
const getTencentTextToVoice = () => {
let audio = document.getElementById("indexAudioId");
// 播放 base64 音频 data:audio/mp3;base64,
/
提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
audio.src = 'data:audio/mp3;base64,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'
*/
// 播放 base64 音频 data:audio/mp3;base64,
audio.addEventListener("canplay", () => {
// 撤消对象URL
URL.revokeObjectURL(audio.src);
});
console.log(audioObj.value);
}
然后放在按钮上面调用可以听到声音就 over 了
调用后端生成接口
按道理除了 text 其它的都是后配置的我们就前端做就行了方便修改
const getTencentTextToVoice = (prompt) => {
tencentTextToVoice({
"text": prompt,
"sessionId": "123156161561561",
"voiceType": 301032,
"codec": "mp3",
"emotionCategory": "happy"
}).then(res => {
if (res.code === 200) {
let audio = document.getElementById("indexAudioId");
// 播放 base64 音频 data:audio/mp3;base64,
audio.src = res.msg
// 播放 base64 音频 data:audio/mp3;base64,
audio.addEventListener("canplay", () => {
console.log("播放完毕")
});
console.log(audioObj.value);
} else {
proxy.$modal.msgError(res.msg);
}
})
}
最后一步给生成画作请求新增语音提示
完毕,自己可以按照之前的流程来进行测试啦
完整视频演示
结束
我们到这里整体的教程就已经完毕啦!!!!!!!
各位大佬辛苦啦看到这里就已经表示你已经完全的学会了腾讯云AI绘画的基本功并且搭建了一个自己的平台项目. 本篇文章以五万两千九百八十六带您玩转腾讯AI绘画图像生成搭建前后端分离项目
如果觉得本篇文章帮助到了您喜欢本篇文章那就点赞支持一下吧!!!!!
仿造的腾讯的在线测试平台,好久没写前端了这么简单的东西写了我一两个小时
最后如果想深入学习AI绘画那么请前往
点击报名,抢先学习《腾讯云AI绘画-StableDiffusion图像生成》训练营 八小时玩转AI绘画
最后
本期结束咱们下次再见👋
🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟 💗
Over the New Year's holiday, the likes of the brush?
我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!