目录
第 1 步:下载 json 文件导入 ComfyUI 修改并保存 API 格式
第 3 步:修改 generate_image 方法、创建对应变量
效果图:
环境搭建:
新建终端,启动虚拟环境 ,安装 grdio 依赖项
pip install requests Pillow gradio numpy #安装grdio依赖项
源码:
- import json
- import os
- import time
- import random
- import gradio as gr
- import requests
- from PIL import Image
-
-
- cached_seed = 0
- PORT_gradio = 4586
- img_count = 0
- URL = "/prompt"
- OUTPUT_DIR = "/ComfyUI/output"
- INPUT_DIR = "/ComfyUI/input_api"
- workflow = "/comfy-gradio-api/json/AIC.json"
- LOADED_MODELS_DIR = "/ComfyUI/models/loras"
-
- def get_available_lora_models():
- lora_models = ["None"]
- for root, dirs, files in os.walk(LOADED_MODELS_DIR):
- for file in files:
- if file.endswith('.safetensors'):
- relative_path = os.path.relpath(os.path.join(root, file), LOADED_MODELS_DIR)
- lora_models.append(relative_path.replace(os.sep, '/'))
- return lora_models
-
- def start_queue(prompt_workflow):
- p = {"prompt": prompt_workflow}
- data = json.dumps(p).encode('utf-8')
- requests.post(URL, data=data)
-
-
- def update_seed(prompt,seed_id,prefix):
- global cached_seed
- if cached_seed == prompt[f"{seed_id}"]["inputs"]["seed"]:
- return get_latest_image_by_prefix(OUTPUT_DIR, f"{prefix}")
- cached_seed = prompt[f"{seed_id}"]["inputs"]["seed"]
-
-
- def preprocess_image(input_image):
- # """
- # 对输入的图像进行预处理,这里主要是将图像等比例缩放,使其最小边为512像素,
- # 返回预处理后的图像对象。
- # """
- image = Image.fromarray(input_image)
- min_side = min(image.size)
- scale_factor = 512 / min_side
- new_size = (round(image.size[0] * scale_factor), round(image.size[1] * scale_factor))
- return image
-
-
- def save_processed_image(processed_image):
- # """
- # 保存预处理后的图像到指定目录(INPUT_DIR),处理文件名冲突问题(通过序号递增保证唯一性),
- # 返回保存后的图像路径。
- # """
- global img_count
- save_name = f'test_api_{str(img_count).zfill(4)}.jpg'
- save_path = os.path.join(INPUT_DIR, save_name)
- while os.path.exists(save_path):
- img_count += 1
- save_name = f'test_api_{str(img_count).zfill(4)}.jpg'
- save_path = os.path.join(INPUT_DIR, save_name)
- processed_image.save(save_path)
- return save_path
-
- def get_latest_image_by_prefix(folder, prefix):
- files = os.listdir(folder)
- image_files = [f for f in files if f.lower().endswith(('.png', '.jpg', '.jpeg')) and f.startswith(prefix)]
- image_files.sort(key=lambda x: os.path.getmtime(os.path.join(folder, x)))
- latest_image = os.path.join(folder, image_files[-1]) if image_files else None
- return latest_image
-
- # 提取文件名中的数字部分
- def get_suffix(image_path):
- return image_path.split('_')[-2]
-
- def wait_for_new_images(*prefixes):
-
- if not prefixes:
- raise ValueError("At least one prefix must be provided.")
-
- previous_images = {prefix: get_latest_image_by_prefix(OUTPUT_DIR, prefix) for prefix in prefixes}
- previous_counts = {prefix: get_suffix(previous_images[prefix]) for prefix in prefixes}
-
- print("Waiting for new images to be generated...")
-
- while True:
- latest_images = {prefix: get_latest_image_by_prefix(OUTPUT_DIR, prefix) for prefix in prefixes}
- latest_counts = {prefix: get_suffix(latest_images[prefix]) for prefix in prefixes}
-
- for prefix in prefixes:
- print(f"{prefix}_count: {latest_counts[prefix]}")
-
- if latest_counts[prefixes[0]] != previous_counts[prefixes[0]] and all(latest_counts[prefix] == latest_counts[prefixes[0]] for prefix in prefixes):
- print("New images detected!")
- print("Previous images:", previous_images)
- print("Latest images:", latest_images)
- return latest_images
-
- time.sleep(1)
-
- def get_example():
- return [
- ["/comfy-gradio-api/example_.jpg", "WaterColor_style, (best quality:2),F1SC_style", 1, "flux/xuxl/动漫风格_动漫风格推文_v1.5.safetensors"]
- ]
-
- def generate_image(input_image,prompt_text,strength_model,lora_name):
- with open(workflow, "r") as file_json:
- prompt = json.load(file_json)
-
- prompt["83"]["inputs"]["text"] = prompt_text
- prompt["76"]["inputs"]["strength_model"] = strength_model
- prompt["76"]["inputs"]["lora_name"] = lora_name
- prompt["55"]["inputs"]["seed"] = random.randint(1, 1000000000000000)
-
- processed_image = preprocess_image(input_image)
- print("save_path = save_processed_image(processed_image)")
- save_path = save_processed_image(processed_image)
- # 将保存后的图像路径更新到工作流配置中相应位置(假设'42'节点需要该图像路径,根据实际调整)
- prompt["88"]["inputs"]["image"] = save_path
-
-
- update_seed(prompt,55,"flux_a")
- # 启动工作流任务队列
- print("start_queue(prompt)")
- start_queue(prompt)
-
- output_images=wait_for_new_images("flux_a")
- return output_images["flux_a"]
-
一、generate_image方法代码修改
第 1 步:下载 json 文件导入 ComfyUI 修改并保存 API 格式
打开ComfyUI,导入 json,检查是否是 save Image 节点,并修改保存输出图像的前缀名 gradio/Flux_lora/xxx
注意:xxx 需要区别与其他重命名文件,例如文件中有 flux_a 前缀命名的图片,xxx 不能只是 flux,要取名为区别与 flux_a 的 flux_b,否则读取的图片会乱。
第 2 步:将 API 格式的 json 保存到服务器,修改URL,workflow等参数
URL:ComfyUI启动地址
INPUT_DIR:ComFyUI 上传图片存放地址
OUTPUT_DIR:图像输出地址
workflow:工作流json保存地址
LOADED_MODELS_DIR:模型保存地址
- URL = "/prompt"
- OUTPUT_DIR = "/ComfyUI/output"
- INPUT_DIR = "/ComfyUI/input_api"
- workflow = "/comfy-gradio-api/json/AIC_.json"
- LOADED_MODELS_DIR = "/ComfyUI/models/loras"
第 3 步:修改 generate_image 方法、创建对应变量
1、加入开放的变量参数,对应修改即可,记得加入形参
注意:如果是文生图,删掉 input_image 和下列 4 行图像处理保存代码即可。
图生图则保留,并修改输入图像对应的 API 号,多图输入时再加 4 行即可。
- processed_image = preprocess_image(input_image)
- # 保存预处理后的图像,并处理文件名冲突问题,获取保存路径
- print("save_path = save_processed_image(processed_image)")
- save_path = save_processed_image(processed_image)
- # 将保存后的图像路径更新到工作流配置中相应位置(假设'42'节点需要该图像路径,根据实际调整)
- prompt["12"]["inputs"]["image"] = save_path
2、seed 种子刷新
大部分情况是都要加的,没有就会一直输出同一张图,找到工作流 json 中对应的 seed 的号码,这里是 55,并修改,输出文件的前缀,flux_b。有的工作流 json 里面可能没有 seed 选项,删掉这行代码即可
3、修改生1图还是生多图
(1)、生成一张图片时,修改一个前缀
修改代码为:
- output_images=wait_for_new_images("flux_a")
- return output_images["flux_a"]
(2)、生成多张图片时,修改返回值加入多个前缀,注意要返回 list
修改代码为:
- output_images = wait_for_new_images("seg1_fg", "seg1_mask", "seg2_fg","seg2_mask")
- output_images_list = [output_images[prefix] for prefix in ["seg1_fg", "seg1_mask", "seg2_fg", "seg2_mask"]]
- return tuple(output_images_list)
二、gradio界面修改
-
- def create_gradio_interface():
- with gr.Blocks() as demo:
- with gr.Row():
- # 图像输入组件和输出图像组件保持平行
- with gr.Column(scale=1):
- image_input = gr.Image(label="输入图像", type="numpy")
- with gr.Column(scale=1):
- output_image = gr.Image(label="输出图像")
- with gr.Row():
- prompt_input = gr.Textbox(label="正向提示词")
- # 创建一行用于其他输入组件
- with gr.Row():
- strength_model = gr.Slider(minimum=0, maximum=1, step=0.01, value=1, label="Lora模型权重")
- lora_name=gr.Dropdown(choices=get_available_lora_models(), label="Lora 模型", info="选择 Lora 模型名")
- # 提交按钮
- submit_button = gr.Button("生成图像")
-
- gr.Examples(
- examples=get_example(),
- fn=generate_image,
- inputs=[image_input, prompt_input, strength_model,lora_name],
- outputs=output_image,
- cache_examples=True,
- )
- # 按钮点击事件绑定
- submit_button.click(
- generate_image,
- inputs=[image_input, prompt_input, strength_model,lora_name],
- outputs=output_image
- )
-
-
-
- demo.launch(
- server_name="0.0.0.0",
- server_port=PORT_gradio,
- allowed_paths=["/ComfyUI/output", "/tmp"]
- )
-
-
- if __name__ == "__main__":
- create_gradio_interface()
1、block 布局,加入或删除需要开放的参数,开始布局。
(1)、gr.Row() 组件水平排列
使用 Row 函数会将组件按照水平排列,但是在 Row 函数块里面的组件都会保持同等高度
(2)、gr.Colum() 组件垂直排列
组件通常是垂直排列,我们可以通过 Row 函数和 Column 函数生成不同复杂的布局。
(3)、gr.Image 图像框
(4)、gr.Textbox 用户输入框
(5)、gr.Slider 值的输入,滑条。value(默认值),
(6)、gr.Dropdown 用户选择框,choices=get_available_lora_models(),提取可选择的模型
2、绑定变量
注意:按钮点击事件绑定变量尽量要与 generate_image 方法形参顺序相同,不然有可能输出不了图像
3、增加 gradio 示例 gr.Examples
添加方法 get_example(),返回的值的顺序要和绑定变量的顺序一样
- def get_example():
- return [
- ["/comfy-gradio-api/example_.jpg", "WaterColor_style, (best quality:2),F1SC_style", 1, "flux/xuxl/动漫风格_动漫风格推文_v1.5.safetensors"]
- ]
- #修改返回值,顺序要和绑定变量的顺序一样
- gr.Examples(
- examples=get_example(),
- fn=generate_image,
- inputs=[image_input, prompt_input, strength_model,lora_name],
- outputs=output_image,
- cache_examples=True,
- )
效果:
4、修改 gradio 访问权限
- demo.launch(
- server_name="0.0.0.0",
- server_port=PORT_gradio,
- allowed_paths=["/ComfyUI/output", "/tmp"]
- )
到这里,恭喜你,就已经初步完成了简单的测试 demo 搭建,如果对你有帮助,期待你的点赞!!!
三、报错解决
错误 1:Gradio 读取图片一直读取不到
情况 1:输出图片的后缀 num 未读取正确,如下图:
原因:ComfyUI 生成图片格式后缀方式偶然会不一样,有时候 00001_.jpg 有时候 00001.jpg
解决方法:修改提取后缀方法,这个改为-1
错误 2:Gradio 生成图像显示问题
情况 1:Gradio 无法将生成的图像文件从移动到 Gradio 的缓存目录
解决方法:修改launch()
方法的allowed_paths
参数: 你可以通过向launch()
方法添加allowed_paths
参数,明确允许 Gradio 访问你的输出目录/home/Intern/liuld/ComfyUI/output
。在你的gr.Interface
的launch()
方法中添加allowed_paths
参数,修改为如下:
- demo.launch(
- server_name="0.0.0.0",
- server_port=PORT_gradio,
- allowed_paths=["/ComfyUI/output", "/tmp"]
- )
这样,Gradio 将允许访问
/ComfyUI/output
目录中的文件。
情况 2:ComUI 的组件后端图像不是保存图像组件,是预览图像组件,gradio 不能找到文件
解决方法:更换组件为保存图像,并保存 json 的 API 格式,打开复制后端的信息将其更换即可
错误 3:端口号占用 
解决方法 1:查询占用端口的进程号:lsof -i :1563
删除后台进程 kill -9 3467673(PID)
解决方法 2:执行 kill.py
修改 gradio 监听端口,运行 kill.py
kill.py代码如下:
- import os
- import signal
- import subprocess
-
- port_pid_gradio = 7315
- def find_process_by_port(port):
- try:
- # 使用 lsof 命令查找占用端口的进程
- command = f"lsof -i :{port}"
- result = subprocess.check_output(command, shell=True, stderr=subprocess.PIPE).decode("utf-8")
-
- # 检查输出是否为空,空表示没有进程占用端口
- if not result:
- print(f"No process found using port {port}.")
- return None
-
- # 获取进程信息,获取进程ID(PID)
- for line in result.splitlines():
- if 'LISTEN' in line: # 只关注处于监听状态的进程
- parts = line.split()
- pid = int(parts[1]) # 获取 PID
- print(f"Process found with PID: {pid}")
- return pid
- except subprocess.CalledProcessError:
- print(f"No process found using port {port}.")
- return None
-
- def kill_process(pid):
- try:
- os.kill(pid, signal.SIGKILL) # 发送 SIGKILL 信号终止进程
- print(f"Process {pid} has been killed successfully.")
- except ProcessLookupError:
- print(f"Process {pid} not found.")
- except PermissionError:
- print(f"No permission to kill process {pid}.")
-
- def main():
-
- pid = find_process_by_port(port_pid_gradio)
- if pid:
- kill_process(pid)
-
- if __name__ == "__main__":
- main()
欢迎 点赞👍 | 收藏⭐ | 评论✍ | 关注🤗