ToB企服应用市场:ToB评测及商务社交产业平台

标题: Stable Diffusion Fooocus批量画图脚本 [打印本页]

作者: 盛世宏图    时间: 2024-12-16 12:23
标题: Stable Diffusion Fooocus批量画图脚本
当当当挡~,流动传热数值计算之余发布点AIGC相干文章,盼望各人能喜好~
1 Stable Diffusion各种UI分析对比

提示:此部门重要是对SD各种界面的简要介绍和对比,只关注Fooocus批量画图的读者可直接跳到第二部门。
Stable Diffusion WebUIForgeUIComfyUIFooocusUI 都是基于 Stable Diffusion(稳定扩散技能)的开源图像天生UI。它们各有特色,满足了不同用户在天生 AI 图像时的需求。
           

      

2 FooocusUI生厂力工具——批量画图脚本

Fooocus擅长快速天生高质量图片,故其在AIGC领域有重要用武之地。依据用户输入的系列关键词批量天生符合要求的图片是SD生产服从的重要指标。截止目前(2024.9.17),WebUI/Forge以及ComfyUI已通过脚本或插件等方式具备了相干能力,但Fooocus的作者似乎并不打算在这方面所有突破。在Fooocus高质高效出图且官方批量画图模块遥遥无期的背景下,我开始尝试在国表里论坛寻找相干题目标解决方案,没想到还真找到了两种(不然我都打算自己造轮子了,难顶)。
   

  2.1 Chrome/Edge油猴脚本

声明:该脚本非原创,本人仅针对博主AidecLi发布的代码进行了一些可能有益的修改(传送门:Fooocus批次執行任務功能(使用tampermonkey實現),此链接在PC端似乎有渲染题目,在手机端能正常查看,但需上网技巧),该博主也在某视频网站发布了脚本教程,各人可观看学习(传送门:Fooocus批次執行任務功能,需上网技巧)。
该方案已被证明可行,而且至少可支持Fooocus2.4.3-2.5.5版本(最新的Fooocus2.5.5版本可在https://github.com/lllyasviel/Fooocus.git下载安装),假如FooocusUI不发生重大改变,那么该脚本应该也会自动适配后续版本(SimpleSDXL2作者对FooocusUI进行了魔改,本脚本暂不支持)。
   

  2.1.1 Javascript脚本

  请务必注意:代码开头两个“// ==UserScript==”间的代码(预设代码)不完全是解释,绝不能删除,有些部门会在代码中生效!!!
预设中的最重要代码为"// @match        http://192.0.0.1:7865/*",该预设限制脚本只能在 IP 为192.0.0.1(即当地地点),端口为7865以及其后所有路径的网页上运行。各人要使用的话必须将这段代码修改为与用户IP和端口匹配的地点(就是打开Fooocus后浏览器网址栏表现的地点),否则脚本不会生效!!!
特殊地,"// @match        http://192.0.0.1:7865/*"中的“*”表示通配符,假如用户使用秋叶启动或自定义主题,浏览器网址栏除了IP和端口外还有如“?__theme=dark”这种主题信息。若不使用通配符,一旦用户更换了主题,则必须重新更新脚本中的网址信息,否则脚本将失效。
别的部门则没什么好说的,各人配合解释应该能明白。
  
  1. // ==UserScript==
  2. // @name         Fooocus批量绘图
  3. // @namespace    https://blog.aidec.tw/ || https://blog.csdn.net/liuqihang11?type=blog
  4. // @version      2024-09-17
  5. // @description  输入以句号结尾的多行提示词实现Fooocus批量绘图,提示词格式:一行为一组,末尾用英文句号隔开。
  6. // @author       AidecLi Create || Syphomn Modify
  7. // @match        http://192.0.0.1:7865/*
  8. // @icon         https://www.google.com/s2/favicons?sz=64&domain=0.1
  9. // @grant        none
  10. // ==/UserScript==
  11. (function() {
  12.     'use strict';
  13.     let isProcessing = false;
  14.     let stopRequested = false;
  15.     let promptInputTextarea, processedTextarea, startButton, stopButton, clearButton, progressBar, statusDiv;
  16.     // 添加UI元素
  17.     function addUI() {
  18.         const targetElement = document.querySelector('#component-3');
  19.         if (!targetElement) {
  20.             console.error('目标元素 #component-3 未找到');
  21.             return;
  22.         }
  23.         const container = document.createElement('div');
  24.         container.style.marginTop = '20px';
  25.         container.style.display = 'flex';
  26.         container.style.flexDirection = 'column';
  27.         container.style.gap = '10px';
  28.         // 待处理文本框
  29.         promptInputTextarea = document.createElement('textarea');
  30.         promptInputTextarea.id = 'promptList';
  31.         promptInputTextarea.rows = 5;
  32.         promptInputTextarea.style.width = '100%';
  33.         promptInputTextarea.style.color = 'red'; // 设置文字为红色
  34.         promptInputTextarea.placeholder = 'Unprocessed Prompts';
  35.         // 已处理文本框
  36.         processedTextarea = document.createElement('textarea');
  37.         processedTextarea.id = 'processedList';
  38.         processedTextarea.rows = 5;
  39.         processedTextarea.style.width = '100%';
  40.         processedTextarea.style.color = 'green'; // 设置文字为绿色
  41.         processedTextarea.placeholder = 'Processed Prompts';
  42.         processedTextarea.readOnly = true;
  43.         // 开始批次处理按钮
  44.         startButton = document.createElement('button');
  45.         startButton.textContent = 'Start Batch-Drawing';
  46.         startButton.onclick = processPrompts;
  47.         startButton.style.padding = '5px 10px';
  48.         startButton.style.cursor = 'pointer';
  49.         startButton.className = 'lg secondary type_row svelte-cmf5ev';
  50.         // 停止批次处理按钮
  51.         stopButton = document.createElement('button');
  52.         stopButton.textContent = 'Stop Batch-Drawing';
  53.         stopButton.onclick = stopProcessing;
  54.         stopButton.style.padding = '5px 10px';
  55.         stopButton.style.cursor = 'pointer';
  56.         stopButton.className = 'lg secondary type_row svelte-cmf5ev';
  57.         // 清除已处理文本框按钮
  58.         clearButton = document.createElement('button');
  59.         clearButton.textContent = 'Clear Processed-Text';
  60.         clearButton.onclick = clearProcessed;
  61.         clearButton.style.padding = '5px 10px';
  62.         clearButton.style.cursor = 'pointer';
  63.         clearButton.className = 'lg secondary type_row svelte-cmf5ev';
  64.         clearButton.disabled = true; // 初始化为不可用状态
  65.         // 进度条
  66.         progressBar = document.createElement('progress');
  67.         progressBar.id = 'progressBar';
  68.         progressBar.value = 0;
  69.         progressBar.max = 100;
  70.         progressBar.style.width = '100%';
  71.         // 状态显示
  72.         statusDiv = document.createElement('div');
  73.         statusDiv.id = 'statusDiv';
  74.         statusDiv.textContent = 'Enter Multiple Lines of Prompts Separated by a Period';
  75.         statusDiv.style.fontWeight = 'bold';
  76.         // 将UI元素添加到容器中
  77.         container.appendChild(statusDiv);
  78.         container.appendChild(promptInputTextarea);
  79.         container.appendChild(processedTextarea);
  80.         container.appendChild(startButton);
  81.         container.appendChild(stopButton);
  82.         container.appendChild(clearButton);
  83.         container.appendChild(progressBar);
  84.         targetElement.appendChild(container);
  85.     }
  86.     // 处理批次提示词
  87.     async function processPrompts() {
  88.         if (isProcessing) return;
  89.         isProcessing = true;
  90.         stopRequested = false;
  91.         disableUI();
  92.         // 重新读取未处理文本框中的内容
  93.         const promptList = promptInputTextarea.value.split('\n').filter(p => p.trim() !== '');
  94.         const totalPrompts = promptList.length;
  95.         const positivePromptTextarea = document.querySelector('#positive_prompt textarea');
  96.         const generateButton = document.querySelector('#generate_button');
  97.         let i = 0;
  98.         for (const prompt of promptList) {
  99.             if (stopRequested) break;
  100.             statusDiv.textContent = `正在处理第 ${i + 1} 组,共 ${totalPrompts} 组提示词: ${prompt}`;
  101.             positivePromptTextarea.value = prompt;
  102.             positivePromptTextarea.dispatchEvent(new Event('input', { bubbles: true }));
  103.             console.log('开始绘制' + prompt);
  104.             await new Promise(resolve => setTimeout(resolve, 500));
  105.             generateButton.click();
  106.             await new Promise(resolve => setTimeout(resolve, 500));
  107.             await waitForGeneration(generateButton);
  108.             if (stopRequested) break;
  109.             console.log('结束绘制' + prompt);
  110.             // 将已处理的移到已处理区
  111.             processedTextarea.value += prompt + '\n';
  112.             // 从待处理区移除已处理的提示词
  113.             promptInputTextarea.value = promptList.slice(i + 1).join('\n');
  114.             // 更新进度条
  115.             progressBar.value = ((i + 1) / totalPrompts) * 100;
  116.             // 等待2秒再继续下一个提示词
  117.             statusDiv.textContent = `第 ${i + 1} 组已处理完毕,即将进入下一组。`;
  118.             await new Promise(resolve => setTimeout(resolve, 1000));
  119.             i++;
  120.         }
  121.         if (stopRequested) {
  122.             statusDiv.textContent = '批量绘图已停止';
  123.         } else {
  124.             statusDiv.textContent = '所有提示词均处理完毕';
  125.             clearButton.disabled = false; // 处理完毕后启用清除按钮
  126.         }
  127.         isProcessing = false;
  128.         enableUI();
  129.     }
  130.     // 停止处理
  131.     function stopProcessing() {
  132.         stopRequested = true;
  133.         isProcessing = false;
  134.         enableUI();
  135.         // 也停止本轮正在运作的生图
  136.         const stopButton = document.querySelector('#stop_button');
  137.         stopButton.click();
  138.         // 启用清除按钮
  139.         clearButton.disabled = false;
  140.     }
  141.     // 清除已处理文本框的内容
  142.     function clearProcessed() {
  143.         processedTextarea.value = '';
  144.         clearButton.disabled = true; // 清除后禁用清除按钮
  145.     }
  146.     // 禁用UI元素
  147.     function disableUI() {
  148.         promptInputTextarea.disabled = true;
  149.         startButton.disabled = true;
  150.         startButton.textContent = 'Processing...';
  151.     }
  152.     // 启用UI元素
  153.     function enableUI() {
  154.         promptInputTextarea.disabled = false;
  155.         startButton.disabled = false;
  156.         startButton.textContent = 'Start Batch-Drawing';
  157.     }
  158.     // 等待生成过程
  159.     function waitForGeneration(button) {
  160.         return new Promise(resolve => {
  161.             const observer = new MutationObserver(() => {
  162.                 if (!button.disabled && !button.hidden) {
  163.                     // 等待按钮
  164.                     setTimeout(() => {
  165.                         observer.disconnect();
  166.                         resolve();
  167.                     }, 500);
  168.                 }
  169.             });
  170.             observer.observe(button, { attributes: true, attributeFilter: ['disabled', 'hidden'] });
  171.             if (stopRequested) resolve();
  172.             // 如果按钮可用且没有hidden,则继续
  173.             if (!button.disabled && !button.hidden) {
  174.                 observer.disconnect();
  175.                 resolve();
  176.             }
  177.         });
  178.     }
  179.     // 将自定义UI添加到页面上
  180.     function initScript() {
  181.         const maxAttempts = 10;
  182.         let attempts = 0;
  183.         function tryAddUI() {
  184.             if (document.querySelector('#component-3')) {
  185.                 addUI();
  186.             } else if (attempts < maxAttempts) {
  187.                 attempts++;
  188.                 setTimeout(tryAddUI, 1000); // 等待1秒重新尝试
  189.             } else {
  190.                 console.error('在 ' + maxAttempts + ' 次尝试后未能找到 #component-3');
  191.             }
  192.         }
  193.         tryAddUI();
  194.     }
  195.     // 页面完成加载UI到页面
  196.     if (document.readyState === 'loading') {
  197.         window.addEventListener('DOMContentLoaded', initScript);
  198.     } else {
  199.         initScript();
  200.     }
  201. })();
复制代码
2.1.2 What's New

   

  ​相比于博主AidecLi的版本,本文提供代码在以下方面做了一些工作:
   
  2.1.3 使用方法


Edge:

Chrome:


           

      
           

      

  
  1. 1girl.
  2. 2girls.
  3. 3girls.
复制代码

OK了各位,Hope you all enjoy it!
2.2 接管Fooocus接口

这是SD各种UI实现批量画图的通法,也是第三方软件调用SD画图的常用做法。该方法需要对SD源代码有根本了解,至少需要掌握SD大模型、lora、高清放大等接口的调用格式(我一开始本来打算采用这种方法,但苦于无法找到具体的Fooocus接口阐明文档,就一直没搞成),幸亏CSDN上的桥呗博主已发布了相干代码供各人学习使用(传送门:fooocus读取批量prompts进行批量天生图像方法)。
该方法是非UI界面画图,有一定使用门槛,且有效性我没有验证,各人可以自行实践。
3 写在最后

本文提供了一个用于Fooocus批量画图的简易脚本,可以或许根本满足用户需要,盼望能帮到各人。
文章已提供源代码,代码1.0版本由博主AidecLi无偿提供,本文对其功能进行了增改,更新到了2.0版本。
AIGC领域的发展需要各人共同努力,欢迎各位对我提供的脚本二次开发,修复Bug,完满功能。
严正声明:

最后再来几张AIGC美图:
   

   
  1. (masterpiece, top quality, best quality, official art, beautiful and aesthetic:1.2),(1girl),extreme detailed,(fractal art:1.3),colorful,highest detailed,
  2. Negative prompt: (worst quality, low quality:2),monochrome,zombie,overexposure,watermark,text,bad anatomy,bad hand,extra hands,extra fingers,too many fingers,fused fingers,bad arm,distorted arm,extra arms,fused arms,extra legs,missing leg,disembodied leg,extra nipples,detached arm,liquid hand,inverted hand,disembodied limb,small breasts,loli,oversized head,extra body,completely nude,extra navel,EasyNegative,(hair between eyes),sketch,duplicate,ugly,huge eyes,text,logo,worst face,(bad and mutated hands:1.3),(blurry:2),horror,geometry,bad_prompt,(bad hands),(missing fingers),multiple limbs,bad anatomy,(interlocked fingers:1.2),Ugly Fingers,(extra digit and hands and fingers and legs and arms:1.4),((2girl)),(deformed fingers:1.2),(long fingers:1.2),(bad-artist-anime),bad-artist,bad hand,extra legs,(ng_deepnegative_v1_75t),
  3. Steps: 30, Sampler: DPM++ 2M Karras, CFG scale: 6, Seed: 1015468391, Size: 512x768, Model hash: e3edb8a26f, Model: GhostMix鬼混_V2.0, Denoising strength: 0.5, Clip skip: 2, Hires upscale: 2, Hires upscaler: R-ESRGAN 4x+ Anime6B, Version: f0.0.17v1.8.0rc-latest-277-g0af28699
复制代码
   

   
  1. modern,masterpiece,best quality,8k,intricate details,hyperdetailed,hyper quality,anime,CG,1girl,modern,
  2. Negative prompt: NSFW,watermark,third-party watermark,character watermark,sharp,(worst quality, low quality:1.4),cropped,poorly drawn,low resolution,logo,text,blurry,bad anatomy,lowres,monochrome,grayscale,(signature:1.4),bad proportions,username,poorly drawn face,malformed hands,skin blemishes,skin spots,bad body,acnes,missing fingers,long body,missing limb,fused fingers,too many fingers,long neck,floating limbs,extra legs,disconnected limbs,bad feet,mutated,cross-eyed,extra arms,ugly,mutated hands,disfigured,poorly drawn hands,(deformed iris, deformed pupils:1.3),(deformed, distorted, disfigured:1.3),out of frame,extra limb,cloned face,amputation,blurry,jpeg artifacts,
  3. Steps: 20, Sampler: DPM++ 2M Karras, CFG scale: 7, Seed: 3081904287, Size: 768x512, Model hash: d6301c47f5, Model: 小说推文大模型-通用, VAE hash: 735e4c3a44, VAE: vae-ft-mse-840000-ema-pruned.safetensors, Denoising strength: 0.7, Hires upscale: 2, Hires upscaler: R-ESRGAN 4x+ Anime6B, Version: f0.0.17v1.8.0rc-latest-277-g0af28699
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4