【生成模型】【ComfyUI(三)】使用WebAPI批量调用ComfyUI

news/2025/2/27 10:57:36

可以参考【生成模型】【ComfyUI(一)】Flux与Flux-Fill部署与API调用中Flux-Fill部分

1. 调整Workflow

我们要部署以下workflow
在这里插入图片描述
做两个修改

  • 输入改为从Load Image(Base64) 读入图片,当然使用上面的从路径中读图也是可以的
  • 输出改为SaveImageWebsocket节点,通过websocket返回图片,当然使用SaveImage给定路径也是可以的
    ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d2e2f42f33df4567a63750900590cb75.png

2. 导出API调用json文件

3. 使用接口调用

import requests, websocket
import base64, io
from PIL import Image, ImageColor

def buffer2img(imagebuf, mode='RGB', input_type='base64'):
    if input_type == 'base64':
        buf = base64.b64decode(imagebuf)
    elif input_type == 'bytes':
        buf = imagebuf
    else:
        raise ValueError(f"input_type should in ['base64', 'bytes'], but got {input_type}")
    pil_img = Image.open(io.BytesIO(buf)).convert(mode)
    return pil_img

def get_server_address():
    server_address = "127.0.0.1:8081"
    return server_address


class ComfyUIRequest:
    def __init__(self, progress_bar_node_type=["KSampler"]):
        self.progress_bar_node_type = progress_bar_node_type

    def queue_prompt(self, prompt, client_id, server_address):
        """
            将任务提交给server_address上的ComfyUI,进入处理队列,同时获得返回的trace_id
        """
        p = {"prompt": prompt, "client_id": client_id}
        # data = json.dumps(p).encode('utf-8')
        # req =  urllib.request.Request("http://{}/prompt".format(server_address), data=data)
        # json.loads(urllib.request.urlopen(req).read())
        response = requests.post(f"http://{server_address}/prompt", json=p)
        if response.status_code == 200:
            return response.json()
        else:
            logger.error(f"{response}, {response.text}")
            # print(f"{response}, {response.text}")
            if response.status_code == 403:
                # print("get <403>, please check your proxy")
                logger.error("get <403>, please check your proxy")
            return None
    
    def get_images_from_web_socket(self, prompt, prompt_id, client_id, server_address):
        ws = websocket.WebSocket()
        ws.connect("ws://{}/ws?clientId={}".format(server_address, client_id))
        # progress_bar = ProgressBar(prompt, self.progress_bar_node_type)
        
        output_images = {}
        current_node = ""
        while True:
            out = ws.recv()
            # logger.debug(out, type(out))
            print(out, type(out))
            # {"type": "progress", "data": {"value": 49, "max": 50, "prompt_id": "cfd0c1cb-04ae-490d-8ed2-6b16bf103da9", "node": "3"}} <class 'str'>
            # {"type": "progress", "data": {"value": 50, "max": 50, "prompt_id": "cfd0c1cb-04ae-490d-8ed2-6b16bf103da9", "node": "3"}} <class 'str'>
            # {"type": "executing", "data": {"node": "8", "display_node": "8", "prompt_id": "cfd0c1cb-04ae-490d-8ed2-6b16bf103da9"}} <class 'str'>
            if isinstance(out, str):
                message = json.loads(out)
                if message['type'] == 'executing': 
                    data = message['data']
                    if 'prompt_id' in data and data['prompt_id'] == prompt_id:
                        if data['node'] is None:
                            break #Execution is done
                        else:
                            current_node = data['node']
                # progress_bar.update(message)
            else:
                if current_node in prompt and prompt[current_node]["class_type"] == "SaveImageWebsocket":
                    images_output = output_images.get(current_node, [])
                    images_output.append(out[8:])
                    output_images[current_node] = images_output
    
        return output_images

    def __call__(self, prompt, client_id, server_address):
        if client_id is None:
            client_id = str(uuid.uuid4())
        if server_address is None:
            server_address = get_server_address()
            
        req_data = self.queue_prompt(prompt, client_id, server_address)  # 发起请求
        if req_data is not None:  # 发起请求成功
            logger.info(f"req to {req_data}")
            out_images = self.get_images_from_web_socket(prompt, req_data['prompt_id'], client_id, server_address)  # 阻塞式接受返回
            return out_images
        return None



comfyui_reqest = ComfyUIRequest()

import json

defalut_params = json.load(open("05_flux_fill_outpaint_fp8_3.json"))
defalut_params["17"]["inputs"]["image"] = os.path.abspath("524169.jpg")    # input image has mul masked
defalut_params["47"]["inputs"]["image"] = os.path.abspath("524169_mask.jpg")  # mask image

outs = comfyui_reqest(defalut_params, client_id="12347")
result_img = buffer2img(outs['50'][0], input_type='bytes')

print(f"req to {req_data}")打印如下:

req to {'prompt_id': '024d4de7-258b-461c-95fa-a0de3f2fefb0', 'number': 45, 'node_errors': {}}

print(out, type(out))打印如下

{"type": "status", "data": {"status": {"exec_info": {"queue_remaining": 1}}, "sid": "16745628-e6ae-46a9-99a3-fa3a5d53f586"}} <class 'str'>
{"type": "execution_cached", "data": {"nodes": ["7", "23", "26", "31", "32", "34", "38", "39", "55", "56", "57", "58"], "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722", "timestamp": 1740473304127}} <class 'str'>
{"type": "executed", "data": {"node": "58", "display_node": "58", "output": {"images": [{"filename": "ComfyUI_00022_.png", "subfolder": "", "type": "output"}]}, "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722"}} <class 'str'>
{"type": "executed", "data": {"node": "57", "display_node": "57", "output": {"images": [{"filename": "ComfyUI_temp_ukmnl_00001_.png", "subfolder": "", "type": "temp"}]}, "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722"}} <class 'str'>
{"type": "executing", "data": {"node": "52", "display_node": "52", "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722"}} <class 'str'>
{"type": "progress", "data": {"value": 1, "max": 20, "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722", "node": "52"}} <class 'str'>
{"type": "progress", "data": {"value": 2, "max": 20, "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722", "node": "52"}} <class 'str'>
{"type": "progress", "data": {"value": 3, "max": 20, "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722", "node": "52"}} <class 'str'>
......
{"type": "progress", "data": {"value": 19, "max": 20, "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722", "node": "52"}} <class 'str'>
{"type": "progress", "data": {"value": 20, "max": 20, "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722", "node": "52"}} <class 'str'>
{"type": "executing", "data": {"node": "8", "display_node": "8", "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722"}} <class 'str'>
{"type": "executing", "data": {"node": "50", "display_node": "50", "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722"}} <class 'str'>
{"type": "progress", "data": {"value": 0, "max": 1, "prompt_id": "eb77c8c4-40d5-4dc0-a6a9-234c25cb4722", "node": "50"}} <class 'str'>

4. 增加调用进度条显示

ComfyUI运行的时候,我们会发现界面上的运行框等信息会变化,这些都是ComfyUI在执行的每个阶段都会发送socket消息,就像上面打印的日志一样,我们可以捕获到这些日志,并增加一些实时的响应。

比如,根据上面日志,增加一个进度条功能,然后解开ComfyUIRequest中与ProgressBar相关的两行代码的注释即可。

from tqdm import tqdm

class ProgressBar(object):
    def __init__(self, prompt, check_node_types=["KSampler"]):
        self.prompt = prompt
        self.progress_bars = {}
        self.check_node_types = check_node_types

    def update(self, message):
        # {"type": "progress", "data": {"value": 49, "max": 50, "prompt_id": "cfd0c1cb-04ae-490d-8ed2-6b16bf103da9", "node": "3"}} <class 'str'>
        # {"type": "progress", "data": {"value": 50, "max": 50, "prompt_id": "cfd0c1cb-04ae-490d-8ed2-6b16bf103da9", "node": "3"}} <class 'str'>
        # {"type": "executing", "data": {"node": "8", "display_node": "8", "prompt_id": "cfd0c1cb-04ae-490d-8ed2-6b16bf103da9"}} <class 'str'>  
        try:
            if message['type'] == 'progress':  # progress
                node = message["data"]['node']
                if node in self.progress_bars:
                    self.progress_bars[node].update(1)
                else:
                    if node is not None and node in self.prompt and \
                        (self.check_node_types is None or self.prompt[node]["class_type"] in self.check_node_types):
                        max_d, cur_d = message["data"]["max"], message["data"]["value"]
                        bar = self.progress_bars[node] = tqdm(total=max_d, desc=self.prompt[node]["_meta"]["title"])
                        bar.update(1)
        except KeyboardInterrupt as e:
            raise e
        except Exception as e:
            pass

http://www.niftyadmin.cn/n/5869997.html

相关文章

深入理解 Spring 中的 `ThreadPoolTaskExecutor` 与 `ThreadPoolExecutor`

在 Spring 框架中&#xff0c;线程池是处理并发任务的核心工具之一。特别是在异步任务的执行中&#xff0c;Spring 提供了 Async 注解来方便地将方法执行移交给线程池。虽然 ThreadPoolExecutor 是 Java 标准库中提供的线程池实现&#xff0c;但 Spring 提供了一个更加符合其生…

Arcgis 实用制图技巧--如何制作“阴影”效果

今天给大家介绍arcgis中阴影效果的制作方法,操作很简单,在ArcMap当中使用制图表达和移动几何方式就可以轻松实现。 左侧地图的图形背景组织很差。右侧地图通过使用阴影效果突出了重点内容。今天,我将要介绍两种阴影效果的创建方法:第一,纯色阴影(single color);第二,渐变…

配置 tabBar 效果

1 创建 tabBar 分支 运行如下的命令&#xff0c;基于 master 分支在本地创建 tabBar 子分支&#xff0c;用来开发和 tabBar 相关的功能&#xff1a; git checkout -b tabbar 2 创建 tabBar 页面 在 pages 目录中&#xff0c;创建首页 (home) 、分类 (cate) 、购物车…

C++ 根据二叉树创建字符串 - 力扣(LeetCode)

点击链接即可查看题目: 606. 根据二叉树创建字符串 - 力扣&#xff08;LeetCode&#xff09; 一、题目 给你二叉树的根节点 root &#xff0c;请你采用前序遍历的方式&#xff0c;将二叉树转化为一个由括号和整数组成的字符串&#xff0c;返回构造出的字符串。 空节点使用一对空…

(九)axios的使用

1、axios 的基本使用 1.1、简介 在 Web 开发的演进历程中&#xff0c;数据请求方式的变革至关重要。回溯早期&#xff0c;旧浏览器在向服务器请求数据时&#xff0c;存在严重弊端。由于返回的是整个页面数据&#xff0c;每次请求都会导致页面强制刷新&#xff0c;这不仅极大地…

VidSketch:具有扩散控制的手绘草图驱动视频生成

浙大提出的VidSketch是第一个能够仅通过任意数量的手绘草图和简单的文本提示来生成高质量视频动画的应用程序。该方法训练是在单个 RTX4090 GPU 上进行的&#xff0c;针对每个动作类别使用一个小型、高质量的数据集。VidSketch方法使所有用户都能使用简洁的文本提示和直观的手绘…

0x01 html和css

css 对于三种css使用方式&#xff1a; 第一种&#xff1a;行内样式 <span style"color: grey;">2024年05月15日 20:07</span>第二种&#xff1a;内部样式 <!DOCTYPE html> <html lang"en"> <head>...<style>span{…

论软件设计模式及其应用-软考

软件设计模式(Software Design Pattern)是一套被反复使用的、多数人知晓的代码设计经验的总结。使用设计模式是为了重用代码以提高编码效率、增加代代码可理解性、保证代码的可靠性。软件设计模式是软件开发中的最佳实践之一,它经常被开发人员在面向对象软件开发过程中所采用…