【使用 ComfyUI 封装文生图 python 接口】

参考文章: 使用python调用comfyui-api,实现出图自由

环境要求

  1. 操作系统
    Windows
  2. python版本
    3.9
  3. 运行中的 ComfyUI 服务
    ComfyUI 部署 Kolors 教程链接: Linux 使用 ComfyUI 本地部署快手可图 Kolors 大模型
    在这里插入图片描述

导出 API json 文件

  1. 编辑工作流完成后,导出 API 文件,如图:
    在这里插入图片描述

使用 flask 封装接口

  1. 使用 pycharm 新建 python 项目 ComfyUIService
  2. 在 pycharm 终端安装依赖
pip install flask, websocket-client

在这里插入图片描述

  1. 创建 app.py 文件
import json
import os
import websocket  # NOTE: websocket-client (https://github.com/websocket-client/websocket-client)
import uuid
import urllib.request
import urllib.parse
import random
from flask import Flask, request, jsonify, send_file

app = Flask(__name__)
# 设置工作目录和项目相关的路径
client_id = str(uuid.uuid4())  # 生成一个唯一的客户端ID
WORKING_DIR = 'output'
SageMaker_ComfyUI = WORKING_DIR
COMFYUI_ENDPOINT = '10.213.13.246:8188'
server_address = COMFYUI_ENDPOINT
workflowfile = 'C:/Users/PC0214/Downloads/kolors_example_api_v3.json'
kolors_text_encode_key = '12'
kolors_sampler_key = '14'


# 定义一个函数来显示GIF图片
def show_gif(fname):
    import base64
    from IPython import display
    with open(fname, 'rb') as fd:
        b64 = base64.b64encode(fd.read()).decode('ascii')
    return display.HTML(f'<img src="data:image/gif;base64,{b64}" />')


# 定义一个函数向服务器队列发送提示信息
def queue_prompt(prompt):
    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)
    return json.loads(urllib.request.urlopen(req).read())


# 定义一个函数来获取图片
def get_image(filename, subfolder, folder_type):
    data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
    url_values = urllib.parse.urlencode(data)
    with urllib.request.urlopen("http://{}/view?{}".format(server_address, url_values)) as response:
        return response.read()


# 定义一个函数来获取历史记录
def get_history(prompt_id):
    with urllib.request.urlopen("http://{}/history/{}".format(server_address, prompt_id)) as response:
        return json.loads(response.read())


# 定义一个函数来获取图片,这涉及到监听WebSocket消息
def get_images(ws, prompt):
    prompt_id = queue_prompt(prompt)['prompt_id']
    print('prompt')
    print(prompt)
    print('prompt_id:{}'.format(prompt_id))
    output_images = {}
    while True:
        out = ws.recv()
        if isinstance(out, str):
            message = json.loads(out)
            if message['type'] == 'executing':
                data = message['data']
                if data['node'] is None and data['prompt_id'] == prompt_id:
                    print('执行完成')
                    break  # 执行完成
        else:
            continue  # 预览为二进制数据

    history = get_history(prompt_id)[prompt_id]
    print(history)
    for o in history['outputs']:
        for node_id in history['outputs']:
            node_output = history['outputs'][node_id]
            # 图片分支
            if 'images' in node_output:
                images_output = []
                for image in node_output['images']:
                    image_data = get_image(image['filename'], image['subfolder'], image['type'])
                    images_output.append(image_data)
                output_images[node_id] = images_output
            # 视频分支
            if 'videos' in node_output:
                videos_output = []
                for video in node_output['videos']:
                    video_data = get_image(video['filename'], video['subfolder'], video['type'])
                    videos_output.append(video_data)
                output_images[node_id] = videos_output
    print('获取图片完成')
    print(output_images)
    return output_images


# 解析工作流并获取图片
def parse_worflow(ws, prompt, seed, workflowfile, width, height):
    workflowfile = workflowfile
    print('workflowfile:'+workflowfile)
    with open(workflowfile, 'r', encoding="utf-8") as workflow_api_txt2gif_file:
        prompt_data = json.load(workflow_api_txt2gif_file)
        # 设置文本提示
        print(f'生成图片关键词是:{prompt}')
        prompt_data[kolors_text_encode_key]["inputs"]["prompt"] = prompt
        prompt_data[kolors_sampler_key]["inputs"]["width"] = width
        prompt_data[kolors_sampler_key]["inputs"]["height"] = height
        prompt_data[kolors_sampler_key]["inputs"]["seed"] = seed
        return get_images(ws, prompt_data)


# 生成图像并显示
def generate_clip(prompt, seed, workflowfile, idx, width, height):
    print('seed:'+str(seed))
    ws = websocket.WebSocket()
    ws.connect("ws://{}/ws?clientId={}".format(server_address, client_id))
    images = parse_worflow(ws, prompt, seed, workflowfile, width, height)

    output_images = []
    for node_id in images:
        for image_data in images[node_id]:
            from datetime import datetime
            # 获取当前时间,并格式化为 YYYYMMDDHHMMSS 的格式
            timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
            # 使用格式化的时间戳在文件名中
            GIF_LOCATION = "{}/{}_{}_{}.png".format(SageMaker_ComfyUI, idx, seed, timestamp)
            print('GIF_LOCATION:'+GIF_LOCATION)
            with open(GIF_LOCATION, "wb") as binary_file:
                # 写入二进制文件
                binary_file.write(image_data)
            # show_gif(GIF_LOCATION)
            output_images.append(GIF_LOCATION)
            print("{} DONE!!!".format(GIF_LOCATION))
    return output_images


@app.route('/generateImgByPrompt', methods=["POST"])
def generate_img_by_prompt():
    params = request.get_json()
    if params is None:
        return "参数不能为空", 400
    prompt = params.get('prompt')
    width = params.get('width')
    height = params.get('height')
    if prompt is None or width is None or height is None:
        return "prompt|width|height 都不能为空", 400

    # 1000102404233412
    seed = random.randint(1, 1000000000000)
    idx = 1
    output_images = generate_clip(prompt, seed, workflowfile, idx, width, height)
    output_image = output_images[0]
    try:
        response = send_file(output_image)
        # os.remove(output_image)
        return response
    except FileNotFoundError:
        return "文件不存在", 404


if __name__ == '__main__':
    app.run(host='0.0.0.0',
            threaded=True,
            debug=False,
            port=6663)

  1. 修改 app.py 配置
COMFYUI_ENDPOINT = '10.213.13.246:8188' # 你的 ComfyUI 服务的地址
workflowfile = 'C:/Users/PC0214/Downloads/kolors_example_api_v3.json' # 前面步骤导出的 API json 文件路径
kolors_text_encode_key = '12' # text encode 下标索引(需要根据你导出的 API json 文件替换)
kolors_sampler_key = '14' # sampler 下标索引(需要根据你导出的 API json 文件替换)

在这里插入图片描述

  1. 启动 app.py
    在这里插入图片描述

调用接口

  1. 接口url
127.0.0.1:6663/generateImgByPrompt
  1. 接口参数(POST请求,json格式)
{
    "prompt": "使用灰黑色调帮我生成一张日系可爱女生真人海报,使用广角镜头把背景拍大点,女生占海报的三分之一大小就好,女生齐肩短发,倚着窗户含笑看着我",
    "width": 512,
    "height": 512
}
  1. 示例
    在这里插入图片描述
### ComfyUI 文生 API 接口文档使用方法 #### 绘接口概述 为了调用ComfyUI的稳定扩散API,特别是针对像生成的功能,需发送HTTP POST请求至`/prompt`路径。此接口用于接收用户的文本提示及其他配置参数来生成相应的像[^1]。 ```json { "prompt": "A beautiful sunset over a mountain range", "aspect_ratio": "16:9", "guidance": 7, "output_format": "png", "output_quality": 90 } ``` 上述JSON对象展示了如何构建一个有效的POST请求体。其中包含了几个重要的字段: - `prompt`: 用户提供的文字描述作为生成片的基础。 - `aspect_ratio`: 片的比例,默认情况下为正方形即“1:1”,但也支持其他比例如这里的“16:9”。 - `guidance`: 控制着最终产出与给定提示之间的一致性和创意性的程度;数值越高越接近原始提示,反之则更加多样化。 - `output_format`: 输出文件格式选项有webp, jpg 和 png三种可供选择。 - `output_quality`: 对于JPEG/WebP而言表示压缩级别(PNG不受影响),取值区间是从0到100,代表最差到最优品质。 #### 安装与部署环境准备 要运行这些API服务端程序,则先得获取并解压名为ComfyUI的应用包。可以从指定位置下载适用于Windows平台带NVIDIA或CPU支持版本的夜间构建压缩档[^2]。 #### 示例代码展示 下面给出了一段Python脚本例子,它利用requests库向服务器提交了一个创建新画作的任务请求,并打印返回的数据结构。 ```python import requests import json url = 'http://localhost:8000/prompt' headers = {'Content-Type': 'application/json'} data = { "prompt": "An abstract painting with vibrant colors and flowing shapes.", "aspect_ratio": "1:1", "guidance": 3.5, "output_format": "jpg", "output_quality": 80 } response = requests.post(url=url, headers=headers, data=json.dumps(data)) print(response.json()) ``` 这段简单的客户端应用程序能够帮助开发者快速上手测试Stable Diffusion API的能力边界以及熟悉其响应模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值