经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 大数据/云/AI » 人工智能基础 » 查看文章
AI绘图开源工具Stable Diffusion WebUI前端API对接
来源:cnblogs  作者:Dragonir  时间:2023/9/1 8:49:05  对本文有异议

背景

本文主要介绍 AI 绘图开源工具 Stable Diffusion WebUIAPI 开启和基本调用方法,通过本文的阅读,你将了解到 stable-diffusion-webui 的基本介绍、安装及 API 环境配置;文生图、图生图、局部重绘、后期处理等 API 接口调用;图像处理开发中常用到一些方法如 Base64PNGCanvasURL 相互转换、Canvas 颜色转换等。

AI 绘图工具目前市面上比较广泛使用的主要有两款,一个是 Midjourney,它提供面向用户有好的操作界面,文生图、图生图等功能非常强大,但是它是一款收费软件;另一个就是开源工具 Stable Diffusion, 同样具有强大的AI绘图和图片再创造能力,但是学习成本和上手难度相对较大,不过由于它是开源的,现在有非常多的用户和开发者,我们可以找到丰富的训练模型和学习资源。本文介绍的 Stable Diffusion WebUI 就是基于 Stable Diffusion 的具有比较完善的可视化操作界面的 AI 绘图开源工具,它的 github 访问地址是 https://github.com/AUTOMATIC1111/stable-diffusion-webui

顺便一提,本文上方的 Banner 图就是使用 Stable Diffusion 生成的 ??

使用体验

在正式开发之前,我们可以先体验一下 Stable Diffusion WebUI 以及两个接口封装和操作界面比较优秀的 AI 绘图网站,了解文生图图生图后期处理等基本操作步骤。

环境配置

安装 Stable Diffusion WebUI

我们首先需要先在本地或者服务器安装部署 Stable Diffusion WebUI,可以从Github克隆仓库,然后按说明文档进行安装,对前端开发来说安装流程非常简单,详细安装流程大家可以自行搜索,现在网上已经有很多保姆级的教程,本文不再赘述。

安装完成后,在 Windows操作系统中进入 stable-diffusion-webui 根目录,然后双击 webui-user.bat 文件即可开启本地运行服务,在浏览器中输入 http://localhost:7860 加载如下所示的界面。在 txt2img 输入框中输入需要生图图像的的正向关键词和反向关键词,点击 Generate 按钮即可生成图像。

完成基本安装后,还可以安装界面汉化插件、关键字中文翻译插件、自己喜欢的风格模型等,都可以按照教程非常容易实现。

开启 API 功能

stable-diffusion-webui 根目录下找到文件 webui-user.bat,使用编辑器打开这个文件,然后在 COMMANDLINE_ARGS 配置项后面添加 --api

  1. set COMMANDLINE_ARGS= --lowvram --precision full --api --listen

然后双击 webui-user.bat 重启服务,此时在浏览器中输入地址 https://localhost:7860/doc,就能看到如下所示的所有接口文档了,我们可以从文档中找到需要接入的接口及详细参数。

?? 我们还可以配置如上所示的 --listen 参数,这样就可以通过局域网访问 stable-diffusion-webui 的接口了。

实现

文生图

文生图接口,可以通过向 stable-diffusion-webui 服务发送正向关键字、反向关键字、图片尺寸、采样步数等参数来调用AI能力生成图片。我们先来看看它有哪些可配置的 payload 参数选项。(暂时只用到后面写了注释的参数,基本上可以满足大部分文生图参数配置)

  1. {
  2. "enable_hr": false, // 开启高清hr
  3. "denoising_strength": 0, // 降噪强度
  4. "hr_scale": 2, // 高清级别
  5. "hr_upscaler": "string",
  6. "hr_second_pass_steps": 0,
  7. "hr_resize_x": 0,
  8. "hr_resize_y": 0,
  9. "hr_sampler_name": "string",
  10. "hr_prompt": "",
  11. "hr_negative_prompt": "",
  12. "prompt": "", // 正向关键字
  13. "styles": [
  14. "string"
  15. ],
  16. "seed": -1, // 随机种子
  17. "subseed": -1, // 子级种子
  18. "subseed_strength": 0, // 子级种子影响力度
  19. "seed_resize_from_h": -1,
  20. "seed_resize_from_w": -1,
  21. "sampler_name": "string",
  22. "batch_size": 1, // 每次生成的张数
  23. "n_iter": 1, // 生成批次
  24. "steps": 50, // 生成步数
  25. "cfg_scale": 7, // 关键词相关性
  26. "width": 512, // 生成图像宽度
  27. "height": 512, // 生成图像高度
  28. "restore_faces": false, // 面部修复
  29. "tiling": false, // 平铺
  30. "do_not_save_samples": false,
  31. "do_not_save_grid": false,
  32. "negative_prompt": "string", // 反向关键字
  33. "eta": 0, // 等待时间
  34. "s_min_uncond": 0,
  35. "s_churn": 0,
  36. "s_tmax": 0,
  37. "s_tmin": 0,
  38. "s_noise": 1,
  39. "override_settings": {}, // 覆盖性配置
  40. "override_settings_restore_afterwards": true,
  41. "script_args": [], // lora 模型参数配置
  42. "sampler_index": "Euler", // 采样方法
  43. "script_name": "string",
  44. "send_images": true, // 是否发送图像
  45. "save_images": false, // 是否在服务端保存生成的图像
  46. "alwayson_scripts": {} // alwayson配置
  47. }

根据需要的参数,在页面逻辑中,我们可以像下面这样实现,此时服务端会根据接口参数,一次生成4张对应的图。接口返回成功之后,我们会接收到一个包含所有图片数据名为 images 的数组,数组项是格式为 base64 的图片,我们可以向下面这样转化为页面上可直接显示的图片。

  1. const response = await txt2img({
  2. id_task: `task(${taskId})`,
  3. // 正向关键词
  4. prompt: 'xxxx',
  5. // 反向关键词
  6. negative_prompt: 'xxxx',
  7. // 随机种子
  8. seed: 'xxxx',
  9. // 生成步数
  10. steps: 7,
  11. // 关键词相关性
  12. cfg_scale: 20,
  13. width: 1024,
  14. height: 1024,
  15. // 每次生成的张数
  16. batch_size: 4,
  17. // 生成批次
  18. n_iter: 1,
  19. // 采样方法
  20. sampler_index: 'xxxx',
  21. // 采用的模型哈希值
  22. sd_model_hash: 'xxxx',
  23. override_settings: {
  24. sd_model_checkpoint: sdModelCheckpoint,
  25. eta_noise_seed_delta: 0.0,
  26. CLIP_stop_at_last_layers: 1.0,
  27. },
  28. })
  29. if (response.status === 200 && response.data) {
  30. try {
  31. const images = response.data.images;
  32. if (images.length === 0) return;
  33. data.imageUrls = images.map(item => `data:image/png;base64,${item}`);
  34. } catch (err) {}
  35. }

生成效果就是本文 Banner 图中的粉色长头发的 3D 卡通风格小姐姐 ?正向关键字大致是 girl, long hair, pink,模型采用的是 3dAnimationDiffusion_v10,其他参数可自行调节。本文后续实例的图生图图优化都将采用这张图作为参考图进行演示。

图生图

图生图接口stable-diffusion-webui 将根据我们从接口传送的参考图,生成内容和风格类似的图片,就像最近抖音上很火的瞬息全宇宙特效一样,也可以将同一张图片通过选择不同模型转化为另一种画风。下面是图生图接口的详细 payload 参数,可以观察到基本上和文生图是一样的,多了一些与参考图片相关的配置,如 init_images

  1. {
  2. "init_images": [
  3. "string"
  4. ],
  5. "resize_mode": 0,
  6. "denoising_strength": 0.75,
  7. "image_cfg_scale": 0,
  8. "mask": "string",
  9. "mask_blur": 0,
  10. "mask_blur_x": 4,
  11. "mask_blur_y": 4,
  12. "inpainting_fill": 0,
  13. "inpaint_full_res": true,
  14. "inpaint_full_res_padding": 0,
  15. "inpainting_mask_invert": 0,
  16. "initial_noise_multiplier": 0,
  17. "prompt": "",
  18. "styles": [
  19. "string"
  20. ],
  21. "seed": -1,
  22. "subseed": -1,
  23. "subseed_strength": 0,
  24. "seed_resize_from_h": -1,
  25. "seed_resize_from_w": -1,
  26. "sampler_name": "string",
  27. "batch_size": 1,
  28. "n_iter": 1,
  29. "steps": 50,
  30. "cfg_scale": 7,
  31. "width": 512,
  32. "height": 512,
  33. "restore_faces": false,
  34. "tiling": false,
  35. "do_not_save_samples": false,
  36. "do_not_save_grid": false,
  37. "negative_prompt": "string",
  38. "eta": 0,
  39. "s_min_uncond": 0,
  40. "s_churn": 0,
  41. "s_tmax": 0,
  42. "s_tmin": 0,
  43. "s_noise": 1,
  44. "override_settings": {},
  45. "override_settings_restore_afterwards": true,
  46. "script_args": [],
  47. "sampler_index": "Euler",
  48. "include_init_images": false,
  49. "script_name": "string",
  50. "send_images": true,
  51. "save_images": false,
  52. "alwayson_scripts": {}
  53. }

在页面接口逻辑中,我们可以通过以下方法实现,其他参数和文生图都是一样的,我们只需要把参考图以 base64 格式添加到 init_images 数组即可。

  1. const response = await img2img({
  2. // 正向关键词
  3. prompt: prompt,
  4. // 反向关键词
  5. negative_prompt: negativePrompt,
  6. // 初始图像
  7. init_images: [sourceImage.value],
  8. // 尺寸缩放
  9. resize_mode: 0,
  10. // ...
  11. });

我们将文生图示例中的图片作为参考图,然后将大模型切换为 墨幽人造人_v1030,就能得到如下图所示的真人写实风格的图片。

局部重绘

图生图 API 还可以实现图片局部重绘、涂鸦、重绘蒙版、高清放大、图像扩充等效果。本文中,我们简单讲解以下如何实现局部重绘功能。

要实现局部重绘功能,首先我们需要使用 Canvas 创建一个画布,然后通过鼠标移动在画布上涂抹出需要重绘的路径,在实际开发时我们可以实现一个如下图所示的图片重绘编辑器,右侧图片黄色区域 ?? 是经过画笔涂抹需要重绘修改的区域。涂抹功能可以通过以下代码简单实现,像擦除画笔大小颜色选择撤销重做等扩展功能可自行搜索如何实现。

  1. <div>
  2. <canvas id="canvas"></canvas>
  3. <img src="your_image.jpg" id="image">
  4. <button onclick="clearMask()">清除遮罩</button>
  5. </div>
  6. <script>
  7. var canvas = document.getElementById('canvas');
  8. var image = document.getElementById('image');
  9. // 设置canvas宽高与图像尺寸相同
  10. canvas.width = image.width;
  11. canvas.height = image.height;
  12. var ctx = canvas.getContext('2d');
  13. var isDrawing = false; // 是否正在绘制
  14. var lastX = 0;
  15. var lastY = 0;
  16. // 鼠标按下事件处理程序
  17. canvas.addEventListener('mousedown', function(e) {
  18. isDrawing = true;
  19. [lastX, lastY] = [e.offsetX, e.offsetY];
  20. });
  21. // 鼠标移动事件处理程序
  22. canvas.addEventListener('mousemove', function(e) {
  23. if (!isDrawing) return;
  24. drawMask(e.offsetX, e.offsetY);
  25. });
  26. // 鼠标释放事件处理程序
  27. canvas.addEventListener('mouseup', function() {
  28. isDrawing = false;
  29. });
  30. // 绘制遮罩函数
  31. function drawMask(x, y) {
  32. ctx.beginPath();
  33. ctx.moveTo(lastX, lastY);
  34. ctx.lineTo(x, y);
  35. ctx.strokeStyle = 'rgba(0, 0, 0, 1)'; // 设置遮罩颜色(黑色)
  36. ctx.lineWidth = 20; // 设置遮罩宽度
  37. ctx.lineCap = 'round'; // 设置线条末端形状为圆形
  38. ctx.stroke();
  39. [lastX, lastY] = [x, y];
  40. }
  41. // 清除遮罩函数
  42. function clearMask() {
  43. ctx.clearRect(0, 0, canvas.width, canvas.height);
  44. }
  45. </script>

涂抹完成后,我们右键保存图片,可以得到如下图左侧所示的图片。但是 stable-diffusion-webui 需要的 Mask遮罩图是涂改区域为纯白色,非修改区域为黑色,且大小需要和原图一致,如右侧图片所示。此时就需要将得到的左侧透明遮罩层转换为右侧需要的遮罩层图片。

Canvas 遮罩层转换方法可以通过如下方式实现,绘制 Mask 图片,Canvas 使用鼠标绘制黑色图案,导出图片时需要把空白部分变为黑色,绘制线条的部分变为白色,并且需要转换成和原图相同的尺寸。其中两个参数 _canvas 是需要转换的画布,_image 是涂抹的原图。其中用于获取图像原始尺寸的方法 getImageOriginSize 可以在本文末尾方法汇总中查看

  1. export const drawMask = async (_canvas, _image) => {
  2. return new Promise(async(resolve) => {
  3. const canvas = _canvas;
  4. const ctx = canvas.getContext('2d');
  5. const imageData = ctx.getImageData(0, 0, canvas.clientWidth, canvas.clientHeight);
  6. const imageDataContent = imageData.data;
  7. // 修改图像数据中的像素颜色
  8. for (let i = 0; i < imageDataContent.length; i += 4) {
  9. // 判断透明度是否小于阈值
  10. if (imageDataContent[i + 3] < 128) {
  11. // 将透明部分设置为黑色
  12. imageDataContent[i] = 0;
  13. imageDataContent[i + 1] = 0;
  14. imageDataContent[i + 2] = 0;
  15. imageDataContent[i + 3] = 255;
  16. } else {
  17. // 将线条部分设置为白色
  18. imageDataContent[i] = 255;
  19. imageDataContent[i + 1] = 255;
  20. imageDataContent[i + 2] = 255;
  21. imageDataContent[i + 3] = 255;
  22. }
  23. }
  24. // 将修改后的图像数据导出为图片格式
  25. const exportCanvas = document.createElement('canvas');
  26. exportCanvas.width = canvas.clientWidth;
  27. exportCanvas.height = canvas.clientHeight;
  28. const exportCtx = exportCanvas.getContext('2d');
  29. exportCtx.putImageData(imageData, 0, 0);
  30. // mask放大到和原图一致
  31. const size = await getImageOriginSize(_image);
  32. const finalMask = await scaleImage(exportCanvas.toDataURL(), size.width, size.height);
  33. resolve(finalMask);
  34. });
  35. }

然后我们将转换后的遮罩图以 base64 的形式作为 mask 参数添加到接口参数中,添加一些正向关键字等参数进行图生图接口请求,返回结果和上述 API 都是一样的,最终就可以得到下面所示的图像,可以观察到被涂抹区域的花朵消失了 ??

  1. const response = await img2img({
  2. init_images: [sourceImage.value],
  3. mask: data.mask,
  4. // ...
  5. });

?? 本文实现中将用到非常多关于图像转换的方法,,比如PNG转Base64、URL转Base64、Canvas转Base64等,这些方法的具体实现将放在文章末尾的汇总中。

后期处理

后期处理接口,可以实现图片高清放大、裁切、扩充等功能,有批量和单图调整两种接口,如下所示是单图调整接口。我们先来看看单图优化 API 可以接收的参数有哪些:

  1. {
  2. "resize_mode": 0,
  3. "show_extras_results": true,
  4. "gfpgan_visibility": 0,
  5. "codeformer_visibility": 0,
  6. "codeformer_weight": 0,
  7. "upscaling_resize": 2,
  8. "upscaling_resize_w": 512,
  9. "upscaling_resize_h": 512,
  10. "upscaling_crop": true,
  11. "upscaler_1": "None",
  12. "upscaler_2": "None",
  13. "extras_upscaler_2_visibility": 0,
  14. "upscale_first": false,
  15. "image": "" // 原图
  16. }

其他

更多接口

stable-diffusion-webui 还有许多实用的 API 可以调用,比如可以通过进度查询接口实时获取到生成图片的进度、剩余时间、排队状态等、可以通过配置查询接口获取到配置信息、已安装的模型列表等;反向推理接口可以通过已生成的图片反推出图像信息、描述关键字等。具体接口使用方法和参数都可以在 API 文档中查询。

实用方法

在做 Web 图片处理应用时需要用到很多图片相关的实用方法,以下是一些简单汇总。

① Canvas转Base64/PNG

要将 Canvas 转换为 Base64 格式图像数据,可以使用 CanvastoDataURL() 方法。该方法将返回一个包含图像数据的字符串,其中包括图像的类型,如 image/pngBase64 编码的图像数据。

  1. // 获取 Canvas 元素
  2. var canvas = document.getElementById("myCanvas");
  3. var ctx = canvas.getContext("2d");
  4. // 将 Canvas 转换为 Base64 格式的图像数据
  5. var dataURL = canvas.toDataURL();
② 图片URL转Base64
  1. export const convertUrlToBase64 = src => {
  2. return new Promise(resolve => {
  3. const img = new Image()
  4. img.crossOrigin = ''
  5. img.src = src
  6. img.onload = function () {
  7. const canvas = document.createElement('canvas');
  8. canvas.width = img.width;
  9. canvas.height = img.height;
  10. const ctx = canvas.getContext('2d');
  11. ctx.drawImage(img, 0, 0, img.width, img.height);
  12. const ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
  13. const dataURL = canvas.toDataURL('image/' + ext);
  14. resolve(dataURL);
  15. }
  16. });
  17. };
③ Base64转PNG
  1. export const convertBase64ToImage = src => {
  2. return new Promise((resolve) => {
  3. const arr = src.split(',');
  4. const byteString = atob(arr[1]);
  5. const ab = new ArrayBuffer(byteString.length);
  6. const ia = new Uint8Array(ab);
  7. for (let i = 0; i < byteString.length; i++) {
  8. ia[i] = byteString.charCodeAt(i);
  9. }
  10. const file = new File([ab], generateRandomId(), { type: 'image/png' });
  11. resolve(file);
  12. });
  13. }
④ 获取图像原始尺寸
  1. export const getImageOriginSize = (src) => {
  2. return new Promise((resolve) => {
  3. const image = new Image();
  4. image.src = src;
  5. image.onload = () => {
  6. resolve({
  7. width: image.naturalWidth,
  8. height: image.naturalHeight
  9. });
  10. };
  11. });
  12. }
⑤ 等比缩放Base64
  1. export const scaleImage = (src, width, height) => {
  2. return new Promise((resolve) => {
  3. const image = new Image();
  4. image.src = src;
  5. image.onload = () => {
  6. const canvas = document.createElement('canvas');
  7. canvas.width = width;
  8. canvas.height = height;
  9. const ctx = canvas.getContext('2d');
  10. ctx.drawImage(image, 0, 0, width, height);
  11. resolve(canvas.toDataURL());
  12. };
  13. })
  14. };

还有上述文章中提到的将透明彩色 Canvas 转化为 stable-diffusion-webui 局部重绘所需的黑底白色图案方法等。

总结

本文主要包含的知识点包括:

  • stable-diffusion-webui 的基本介绍
  • stable-diffusion-webui 安装及 API 环境配置
  • 文生图、图生图、局部重绘、后期处理等 API 接口调用
  • 图像处理开发中常用到一些方法如 Base64PNGCanvasURL 相互转换、Canvas 颜色转换等

想了解其他前端知识或其他未在本文中详细描述的AI相关开发技术相关知识,可阅读我往期的文章。如果有疑问可以在评论中留言,如果觉得文章对你有帮助,不要忘了一键三连哦 ??

附录

参考

本文作者:dragonir 本文地址:https://www.cnblogs.com/dragonir/p/17668699.html

原文链接:https://www.cnblogs.com/dragonir/p/17668699.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号