文档

AI视频生成-ComfyUI镜像部署

更新时间:

EAS提供了极简部署方式,您只需配置几个参数,即可一键部署基于ComfyUI和Stable Video Diffusion模型的AI视频生成服务,帮助您完成社交平台短视频内容生成、动画制作等任务。本文为您介绍如何基于ComfyUI镜像部署服务和几种常用的调用方式。

背景信息

随着AIGC的广泛应用,AI视频生成已成为当前热门应用之一。目前市面上有许多开源视频生成大模型可供选择,它们在不同领域展现了各自独特的性能。与此同时,AIGC开源工具ComfyUI也迅速在市场上崭露头角。作为一个基于节点流程式的AIGC生成工具WebUI,ComfyUI将AIGC流程拆分成工作节点,实现了精准的工作流定制和可复现性。您可以一键部署基于ComfyUI的AI视频生成服务,支持以下三种版本:

  • 标准版:适用于单用户使用WebUI。支持以下两种调用方式:

    • WebUI:请求发送时,会绕过EAS接口,前端直接将请求传递给后端服务器,所有请求均由同一个后端实例进行处理。

    • API:通过EAS接口发送请求,支持同时调度多个后端实例进行处理。

  • API版:系统将自动转换服务为异步模式,适用于高并发场景。该模式仅支持API异步调用。基于其异步特性,系统会创建队列服务实例,因此需要分配额外的CPU实例。

  • 集群版:适合多用户同时在WebUI页面进行操作。仅支持通过WebUI进行调用,不提供API服务。主要适用于设计组/教学场景,通过分时复用的设计来提升推理集群的利用率,降低成本。由于Proxy负责处理WebUI请求,因此需要分配额外的CPU实例。参考集群版服务原理介绍,了解集群版服务的实现原理。该版本优势如下:

    • 系统为每个用户提供独立的后端环境。当开启了多个实例时,单个用户的任务会按顺序执行,而多用户环境下的任务则在不同实例之间分配,实现高效的GPU共享。

    • 系统为每个用户分配独立的工作目录,便于管理和存储模型、输出图像/视频等文件。

具体使用流程如下:

  1. 部署EAS服务

    根据您的具体使用场景,选择部署标准版、API版或集群版的服务。

  2. 调用EAS服务

    根据部署的服务版本,支持以下三种调用方式:

    • 通过WebUI调用EAS服务

      使用WebUI发送服务请求,仅标准版和集群版服务支持使用该方式。

    • 在线调试EAS服务

      在EAS的在线调试页面发送同步调用请求,只有标准版服务支持同步调用功能。

    • 通过API调用EAS服务

      只有标准版和API版服务支持通过API发送服务请求。其中,标准版服务仅支持同步调用,而API版服务仅支持异步调用。

前提条件

在部署微调模型、使用API调用服务或者使用集群版服务时,您必须挂载NAS或OSS存储,以便上传微调模型和获取推理结果。请提前准备NAS或OSS存储空间:

  • 已创建OSS Bucket和空文件目录,例如:oss://bucket-test/data-oss/,其中:bucket-test为OSS Bucket名称;data-oss为该Bucket下的空文件目录。关于如何创建Bucket,请参见创建存储空间;关于如何创建空目录,请参见管理目录

  • 已创建NAS文件系统和空文件目录。具体操作,请参见创建文件系统

部署EAS服务

  1. 进入模型在线服务页面。

    1. 登录PAI控制台

    2. 在左侧导航栏单击工作空间列表,在工作空间列表页面中单击待操作的工作空间名称,进入对应工作空间内。

    3. 在工作空间页面的左侧导航栏选择模型部署>模型在线服务(EAS),进入模型在线服务页面。image

  2. 模型在线服务页面,单击部署服务,在场景化模型部署区域,单击AI视频生成-ComfyUI部署

  3. AI视频生成-ComfyUI部署页面,配置以下关键参数。

    参数

    描述

    基本信息

    服务名称

    自定义模型服务名称。

    版本选择

    支持选择以下版本:

    • 标准版:适用于单用户使用WebUI。支持通过WebUI生成视频,也可通过API进行调用。

    • API版:系统将自动转换服务为异步模式,适用于高并发场景。仅支持通过API进行调用。

    • 集群版WebUI:适合多用户同时在WebUI页面进行操作。仅支持通过WebUI进行调用,不提供API服务。关于该版本的实现原理介绍,请参见集群版服务原理介绍

    更多关于每个版本的使用场景说明,请参见背景信息

    模型配置

    当选择API版标准版,并通过API进行调用时,需要单击添加按钮,进行模型配置,以便获取推理结果。支持以下两种配置类型:

    • 按对象存储(OSS):单击image选择已创建的OSS存储目录。

    • 按文件存储(NAS):配置NAS挂载点和NAS源路径。

    资源配置

    资源配置选择

    推荐使用GU30、A10或T4卡型。系统默认选择GPU > ml.gu7i.c16m60.1-gu30,性价比高。

  4. 单击部署

调用EAS服务

通过WebUI调用EAS服务

通过WebUI,您可以调用标准版和集群版的EAS服务。在标准版服务中,所有请求都由同一个后端实例处理。而集群版服务则适合多用户同时操作,它能够在多个实例间分配并处理各用户的任务。具体操作步骤如下:

  1. 单击目标服务的服务方式列下的查看Web应用

  2. 在WebUI页面进行模型推理验证。

    根据您自己的业务需要,选择文生图的模型和图生视频的模型,本方案使用默认配置。然后在CLIP文本编码器中输入Prompts,例如:Rocket takes off from the ground, fire, sky, airplane,单击提示词队列, 等待工作流运行完成即可获得AI生成的视频。85453c9fcadd222fbb087c5acddb6e90.png

  3. 右键单击生成的视频,选择保存图像,即可将生成的视频保存到本地。image.png

    生成的视频示例如下所示:

在线调试EAS服务

仅标准版服务支持在线调试,具体操作步骤如下:

  1. 生成请求体。具体操作,请参见如何生成请求体

  2. 模型在线服务页面,单击目标服务操作列下的在线调试,进入在线调试页面。

  3. 发送POST请求,获取Prompt ID。

    1. 在调试页面的在线调试请求参数区域的Body处填写已准备好的请求体。并在请求URL文本编辑框中添加/promptimage

    2. 单击发送请求,即可在调试信息区域查看预测结果,示例如下。image

  4. 发送GET请求,根据Prompt ID获取推理结果。

    1. 在线调试请求参数区域中,将请求方法修改为GET,并在文本框中配置/history/<prompt id>,示例如下。image

      其中<prompt id>需要替换为步骤3获取的Prompt ID。

    2. 单击发送请求,即可获取推理结果。

通过API调用EAS服务

标准版和API版服务支持API调用。API调用支持同步调用和异步调用两种方式:

  • 同步调用

    标准版服务仅支持同步调用方式,即客户端发送一个请求,同步等待结果返回。

  • 异步调用

    API版服务仅支持异步调用方式,即客户端使用EAS的队列服务向输入队列发送请求,并通过订阅的方式从输出队列查询结果。

由于ComfyUI本身具有异步队列系统,即使发起同步调用,实质上也是异步进行的。即当用户提交请求后,系统会返回一个Prompt ID,您需要使用该ID轮询以获取推理结果。

同步调用

  1. 查看调用信息。

    1. 在服务列表中,单击标准版服务名称,进入服务详情页面。

    2. 单击基本信息区域的查看调用信息

    3. 调用信息对话框的公网地址调用页签,获取服务访问地址和Token。image

  2. 获取Prompt ID。

    1. 生成请求体。具体操作,请参见如何生成请求体

    2. 发送请求,获取Prompt ID。

      支持以下两种方式:

      Curl

      • HTTP请求方式:POST

      • 请求URL:<service_url>/prompt

      • 请求头部:

        头部

        描述

        Authorization

        <token>

        授权密钥

        Content-Type

        application/json

        指定请求体格式

      • 代码示例

        curl --location --request POST '<service_url>/prompt' \
        --header 'Authorization: <token>' \
        --header 'Content-Type: application/json' \
        --data-raw '{
            "prompt":
            ...省略
        }'

        其中关键配置项如下:

        配置项

        描述

        <service_url>

        替换为步骤1中获取的服务访问地址。例如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com

        <token>

        替换为步骤1中获取的Token。例如ZGJmNzcwYjczODE1MmVlNWY1NTNiNGYxNDkzODI****NzU2NTFiOA==

        data-raw

        配置为请求体,例如:

        重要

        请求体中的布尔值首字母需要小写。

        单击此处查看请求体示例

        {
            "prompt": {
                "3": {
                    "inputs": {
                        "seed": 367490676387803,
                        "steps": 40,
                        "cfg": 7,
                        "sampler_name": "dpmpp_sde_gpu",
                        "scheduler": "karras",
                        "denoise": 1,
                        "model": [
                            "4",
                            0
                        ],
                        "positive": [
                            "6",
                            0
                        ],
                        "negative": [
                            "7",
                            0
                        ],
                        "latent_image": [
                            "5",
                            0
                        ]
                    },
                    "class_type": "KSampler",
                    "_meta": {
                        "title": "K采样器"
                    }
                },
                "4": {
                    "inputs": {
                        "ckpt_name": "LandscapeBING_v10.safetensors"
                    },
                    "class_type": "CheckpointLoaderSimple",
                    "_meta": {
                        "title": "Checkpoint加载器(简易)"
                    }
                },
                "5": {
                    "inputs": {
                        "width": 720,
                        "height": 1280,
                        "batch_size": 1
                    },
                    "class_type": "EmptyLatentImage",
                    "_meta": {
                        "title": "空Latent"
                    }
                },
                "6": {
                    "inputs": {
                        "text": "Rocket takes off from the ground, fire,sky, airplane",
                        "clip": [
                            "4",
                            1
                        ]
                    },
                    "class_type": "CLIPTextEncode",
                    "_meta": {
                        "title": "CLIP文本编码器"
                    }
                },
                "7": {
                    "inputs": {
                        "text": "",
                        "clip": [
                            "4",
                            1
                        ]
                    },
                    "class_type": "CLIPTextEncode",
                    "_meta": {
                        "title": "CLIP文本编码器"
                    }
                },
                "8": {
                    "inputs": {
                        "samples": [
                            "3",
                            0
                        ],
                        "vae": [
                            "4",
                            2
                        ]
                    },
                    "class_type": "VAEDecode",
                    "_meta": {
                        "title": "VAE解码"
                    }
                },
                "9": {
                    "inputs": {
                        "filename_prefix": "ComfyUI",
                        "images": [
                            "8",
                            0
                        ]
                    },
                    "class_type": "SaveImage",
                    "_meta": {
                        "title": "保存图像"
                    }
                },
                "13": {
                    "inputs": {
                        "seed": 510424455529432,
                        "steps": 40,
                        "cfg": 2.5,
                        "sampler_name": "euler_ancestral",
                        "scheduler": "karras",
                        "denoise": 1,
                        "model": [
                            "17",
                            0
                        ],
                        "positive": [
                            "16",
                            0
                        ],
                        "negative": [
                            "16",
                            1
                        ],
                        "latent_image": [
                            "16",
                            2
                        ]
                    },
                    "class_type": "KSampler",
                    "_meta": {
                        "title": "K采样器"
                    }
                },
                "14": {
                    "inputs": {
                        "samples": [
                            "13",
                            0
                        ],
                        "vae": [
                            "18",
                            2
                        ]
                    },
                    "class_type": "VAEDecode",
                    "_meta": {
                        "title": "VAE解码"
                    }
                },
                "15": {
                    "inputs": {
                        "filename_prefix": "ComfyUI",
                        "fps": 10,
                        "lossless": false,
                        "quality": 85,
                        "method": "default",
                        "images": [
                            "14",
                            0
                        ]
                    },
                    "class_type": "SaveAnimatedWEBP",
                    "_meta": {
                        "title": "保存WEBP"
                    }
                },
                "16": {
                    "inputs": {
                        "width": 512,
                        "height": 768,
                        "video_frames": 35,
                        "motion_bucket_id": 140,
                        "fps": 15,
                        "augmentation_level": 0.15,
                        "clip_vision": [
                            "18",
                            1
                        ],
                        "init_image": [
                            "8",
                            0
                        ],
                        "vae": [
                            "18",
                            2
                        ]
                    },
                    "class_type": "SVD_img2vid_Conditioning",
                    "_meta": {
                        "title": "SVD_图像到视频_条件"
                    }
                },
                "17": {
                    "inputs": {
                        "min_cfg": 1,
                        "model": [
                            "18",
                            0
                        ]
                    },
                    "class_type": "VideoLinearCFGGuidance",
                    "_meta": {
                        "title": "线性CFG引导"
                    }
                },
                "18": {
                    "inputs": {
                        "ckpt_name": "svd_xt_image_decoder.safetensors"
                    },
                    "class_type": "ImageOnlyCheckpointLoader",
                    "_meta": {
                        "title": "Checkpoint加载器(仅图像)"
                    }
                },
                "19": {
                    "inputs": {
                        "frame_rate": 10,
                        "loop_count": 0,
                        "filename_prefix": "comfyUI",
                        "format": "video/h264-mp4",
                        "pix_fmt": "yuv420p",
                        "crf": 20,
                        "save_metadata": true,
                        "pingpong": false,
                        "save_output": true,
                        "images": [
                            "14",
                            0
                        ]
                    },
                    "class_type": "VHS_VideoCombine",
                    "_meta": {
                        "title": "合并为视频"
                    }
                }
            }
        }

      Python

      代码示例如下:

      import requests
      
      url = "<service_url>/prompt"
      
      payload = {
          "prompt":
          ...省略
      }
      
      session = requests.session()
      session.headers.update({"Authorization":"<token>"})
      
      
      response = session.post(url=f'{url}', json=payload)
      if response.status_code != 200:
          raise Exception(response.content)
      
      data = response.json()
      print(data)

      其中关键配置项如下:

      配置项

      描述

      <service_url>

      替换为步骤1中获取的服务访问地址。例如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com

      <token>

      替换为步骤1中获取的Token。ZGJmNzcwYjczODE1MmVlNWY1NTNiNGYxNDkzODI****NzU2NTFiOA==

      payload

      配置为请求体,例如:

      重要

      请求体中的布尔值首字母需要大写。

      单击此处查看请求体示例

      {
          "prompt": {
              "3": {
                  "inputs": {
                      "seed": 367490676387803,
                      "steps": 40,
                      "cfg": 7,
                      "sampler_name": "dpmpp_sde_gpu",
                      "scheduler": "karras",
                      "denoise": 1,
                      "model": [
                          "4",
                          0
                      ],
                      "positive": [
                          "6",
                          0
                      ],
                      "negative": [
                          "7",
                          0
                      ],
                      "latent_image": [
                          "5",
                          0
                      ]
                  },
                  "class_type": "KSampler",
                  "_meta": {
                      "title": "K采样器"
                  }
              },
              "4": {
                  "inputs": {
                      "ckpt_name": "LandscapeBING_v10.safetensors"
                  },
                  "class_type": "CheckpointLoaderSimple",
                  "_meta": {
                      "title": "Checkpoint加载器(简易)"
                  }
              },
              "5": {
                  "inputs": {
                      "width": 720,
                      "height": 1280,
                      "batch_size": 1
                  },
                  "class_type": "EmptyLatentImage",
                  "_meta": {
                      "title": "空Latent"
                  }
              },
              "6": {
                  "inputs": {
                      "text": "Rocket takes off from the ground, fire,sky, airplane",
                      "clip": [
                          "4",
                          1
                      ]
                  },
                  "class_type": "CLIPTextEncode",
                  "_meta": {
                      "title": "CLIP文本编码器"
                  }
              },
              "7": {
                  "inputs": {
                      "text": "",
                      "clip": [
                          "4",
                          1
                      ]
                  },
                  "class_type": "CLIPTextEncode",
                  "_meta": {
                      "title": "CLIP文本编码器"
                  }
              },
              "8": {
                  "inputs": {
                      "samples": [
                          "3",
                          0
                      ],
                      "vae": [
                          "4",
                          2
                      ]
                  },
                  "class_type": "VAEDecode",
                  "_meta": {
                      "title": "VAE解码"
                  }
              },
              "9": {
                  "inputs": {
                      "filename_prefix": "ComfyUI",
                      "images": [
                          "8",
                          0
                      ]
                  },
                  "class_type": "SaveImage",
                  "_meta": {
                      "title": "保存图像"
                  }
              },
              "13": {
                  "inputs": {
                      "seed": 510424455529432,
                      "steps": 40,
                      "cfg": 2.5,
                      "sampler_name": "euler_ancestral",
                      "scheduler": "karras",
                      "denoise": 1,
                      "model": [
                          "17",
                          0
                      ],
                      "positive": [
                          "16",
                          0
                      ],
                      "negative": [
                          "16",
                          1
                      ],
                      "latent_image": [
                          "16",
                          2
                      ]
                  },
                  "class_type": "KSampler",
                  "_meta": {
                      "title": "K采样器"
                  }
              },
              "14": {
                  "inputs": {
                      "samples": [
                          "13",
                          0
                      ],
                      "vae": [
                          "18",
                          2
                      ]
                  },
                  "class_type": "VAEDecode",
                  "_meta": {
                      "title": "VAE解码"
                  }
              },
              "15": {
                  "inputs": {
                      "filename_prefix": "ComfyUI",
                      "fps": 10,
                      "lossless": False,
                      "quality": 85,
                      "method": "default",
                      "images": [
                          "14",
                          0
                      ]
                  },
                  "class_type": "SaveAnimatedWEBP",
                  "_meta": {
                      "title": "保存WEBP"
                  }
              },
              "16": {
                  "inputs": {
                      "width": 512,
                      "height": 768,
                      "video_frames": 35,
                      "motion_bucket_id": 140,
                      "fps": 15,
                      "augmentation_level": 0.15,
                      "clip_vision": [
                          "18",
                          1
                      ],
                      "init_image": [
                          "8",
                          0
                      ],
                      "vae": [
                          "18",
                          2
                      ]
                  },
                  "class_type": "SVD_img2vid_Conditioning",
                  "_meta": {
                      "title": "SVD_图像到视频_条件"
                  }
              },
              "17": {
                  "inputs": {
                      "min_cfg": 1,
                      "model": [
                          "18",
                          0
                      ]
                  },
                  "class_type": "VideoLinearCFGGuidance",
                  "_meta": {
                      "title": "线性CFG引导"
                  }
              },
              "18": {
                  "inputs": {
                      "ckpt_name": "svd_xt_image_decoder.safetensors"
                  },
                  "class_type": "ImageOnlyCheckpointLoader",
                  "_meta": {
                      "title": "Checkpoint加载器(仅图像)"
                  }
              },
              "19": {
                  "inputs": {
                      "frame_rate": 10,
                      "loop_count": 0,
                      "filename_prefix": "comfyUI",
                      "format": "video/h264-mp4",
                      "pix_fmt": "yuv420p",
                      "crf": 20,
                      "save_metadata": True,
                      "pingpong": False,
                      "save_output": True,
                      "images": [
                          "14",
                          0
                      ]
                  },
                  "class_type": "VHS_VideoCombine",
                  "_meta": {
                      "title": "合并为视频"
                  }
              }
          }
      }

      返回结果示例如下:

      {'prompt_id': '021ebc5b-e245-4e37-8bd3-00f7b949****',
       'number': 5,
       'node_errors': {}}

      您可以从返回结果中获取Prompt ID。

  3. 发送请求,获取推理结果。

    支持以下两种方式:

    Curl

    • HTTP请求方式:GET

    • 请求URL:<service_url>/history/<prompt_id>

    • 请求头部:

    • 头部

      描述

      Authorization

      <token>

      授权密钥

    • 代码示例:

      curl --location --request GET '<service_url>/history/<prompt_id>' \
           --header 'Authorization: <token>'

      其中关键配置项如下:

      配置项

      描述

      <service_url>

      替换为步骤1中获取的服务访问地址。例如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com

      <token>

      替换为步骤1中获取的Token。例如ZGJmNzcwYjczODE1MmVlNWY1NTNiNGYxNDkzODI****NzU2NTFiOA==

      <prompt_id>

      替换为步骤2中获取的prompt_id。

    Python

    代码示例如下:

    import requests
    
    # 构造请求URL。
    url = "<service_url>/history/<prompt_id>"
    
    session = requests.session()
    session.headers.update({"Authorization":"<token>"})
    
    response = session.get(url=f'{url}')
    
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    print(data)

    其中关键配置项如下:

    配置项

    描述

    <service_url>

    替换为步骤1中获取的服务访问地址。例如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com

    <token>

    替换为步骤1中获取的Token。例如ZGJmNzcwYjczODE1MmVlNWY1NTNiNGYxNDkzODI****NzU2NTFiOA==

    <prompt_id>

    替换为步骤2中获取的prompt_id。

    返回结果示例如下:

    {
        "130bcd6b-5bb5-496c-9c8c-3a1359a0****": {
            "prompt": ...省略,
            "outputs": {
                "9": {
                    "images": [
                        {
                            "filename": "ComfyUI_1712645398_18dba34d-df87-4735-a577-c63d5506a6a1_.png",
                            "subfolder": "",
                            "type": "output"
                        }
                    ]
                },
                "15": {
                    "images": [
                        {
                            "filename": "ComfyUI_1712645867_.webp",
                            "subfolder": "",
                            "type": "output"
                        }
                    ],
                    "animated": [
                        true
                    ]
                },
                "19": {
                    "gifs": [
                        {
                            "filename": "comfyUI_00002.mp4",
                            "subfolder": "",
                            "type": "output",
                            "format": "video/h264-mp4"
                        }
                    ]
                }
            },
            "status": {
                "status_str": "success",
                "completed": true,
                "messages": ...省略,
            }
        }
    }
    

    在本示例返回的outputs中提供了prompt生成的图像、webp文件和mp4视频,您可以在挂载存储的output目录中,根据文件名称来查找这些文件。

异步调用

仅API版的服务支持异步调用,且仅支持api_prompt路径。

  1. 查看调用信息。

    单击API版服务的服务方式列下的调用信息,在调用信息对话框的异步调用页签,查看服务访问地址和Token。image

  2. 推送请求。

    代码示例如下:

    import requests,io,base64
    from PIL import Image, PngImagePlugin
    
    url = "<service_url>"
    session = requests.session()
    session.headers.update({"Authorization":"<token>"})
    
    work_flow = {
        '3': 
        ...省略
      }
    
    for i in range(5):
      payload = work_flow
      response = session.post(url=f'{url}/api_prompt?task_id=txt2img_{i}', json=payload)
      if response.status_code != 200:
        exit(f"send request error:{response.content}")
      else:
        print(f"send {i} success, index is {response.content}")

    其中关键配置项如下:

    配置项

    描述

    <service_url>

    替换为步骤1获取的服务访问地址。

    <token>

    替换为步骤1获取的Token。

    work_flow

    配置为工作流对应的JSON文件内容,示例如下。如何获取工作流JSON文件,请参见如何生成请求体

    重要

    文件中的布尔值首字母需要大写。

    单击此处查看JSON文件示例

    {
      "3": {
        "inputs": {
          "seed": 1021224598837526,
          "steps": 40,
          "cfg": 7,
          "sampler_name": "dpmpp_sde_gpu",
          "scheduler": "karras",
          "denoise": 1,
          "model": [
            "4",
            0
          ],
          "positive": [
            "6",
            0
          ],
          "negative": [
            "7",
            0
          ],
          "latent_image": [
            "5",
            0
          ]
        },
        "class_type": "KSampler",
        "_meta": {
          "title": "K采样器"
        }
      },
      "4": {
        "inputs": {
          "ckpt_name": "LandscapeBING_v10.safetensors"
        },
        "class_type": "CheckpointLoaderSimple",
        "_meta": {
          "title": "Checkpoint加载器(简易)"
        }
      },
      "5": {
        "inputs": {
          "width": 720,
          "height": 1280,
          "batch_size": 1
        },
        "class_type": "EmptyLatentImage",
        "_meta": {
          "title": "空Latent"
        }
      },
      "6": {
        "inputs": {
          "text": "Rocket takes off from the ground, fire, sky, airplane",
          "clip": [
            "4",
            1
          ]
        },
        "class_type": "CLIPTextEncode",
        "_meta": {
          "title": "CLIP文本编码器"
        }
      },
      "7": {
        "inputs": {
          "text": "",
          "clip": [
            "4",
            1
          ]
        },
        "class_type": "CLIPTextEncode",
        "_meta": {
          "title": "CLIP文本编码器"
        }
      },
      "8": {
        "inputs": {
          "samples": [
            "3",
            0
          ],
          "vae": [
            "4",
            2
          ]
        },
        "class_type": "VAEDecode",
        "_meta": {
          "title": "VAE解码"
        }
      },
      "9": {
        "inputs": {
          "filename_prefix": "ComfyUI",
          "images": [
            "8",
            0
          ]
        },
        "class_type": "SaveImage",
        "_meta": {
          "title": "保存图像"
        }
      },
      "13": {
        "inputs": {
          "seed": 1072245043382649,
          "steps": 40,
          "cfg": 2.5,
          "sampler_name": "euler_ancestral",
          "scheduler": "karras",
          "denoise": 1,
          "model": [
            "17",
            0
          ],
          "positive": [
            "16",
            0
          ],
          "negative": [
            "16",
            1
          ],
          "latent_image": [
            "16",
            2
          ]
        },
        "class_type": "KSampler",
        "_meta": {
          "title": "K采样器"
        }
      },
      "14": {
        "inputs": {
          "samples": [
            "13",
            0
          ],
          "vae": [
            "18",
            2
          ]
        },
        "class_type": "VAEDecode",
        "_meta": {
          "title": "VAE解码"
        }
      },
      "15": {
        "inputs": {
          "filename_prefix": "ComfyUI",
          "fps": 10,
          "lossless": False,
          "quality": 85,
          "method": "default",
          "images": [
            "14",
            0
          ]
        },
        "class_type": "SaveAnimatedWEBP",
        "_meta": {
          "title": "保存WEBP"
        }
      },
      "16": {
        "inputs": {
          "width": 512,
          "height": 768,
          "video_frames": 35,
          "motion_bucket_id": 140,
          "fps": 15,
          "augmentation_level": 0.15,
          "clip_vision": [
            "18",
            1
          ],
          "init_image": [
            "8",
            0
          ],
          "vae": [
            "18",
            2
          ]
        },
        "class_type": "SVD_img2vid_Conditioning",
        "_meta": {
          "title": "SVD_图像到视频_条件"
        }
      },
      "17": {
        "inputs": {
          "min_cfg": 1,
          "model": [
            "18",
            0
          ]
        },
        "class_type": "VideoLinearCFGGuidance",
        "_meta": {
          "title": "线性CFG引导"
        }
      },
      "18": {
        "inputs": {
          "ckpt_name": "svd_xt_image_decoder.safetensors"
        },
        "class_type": "ImageOnlyCheckpointLoader",
        "_meta": {
          "title": "Checkpoint加载器(仅图像)"
        }
      },
      "19": {
        "inputs": {
          "frame_rate": 10,
          "loop_count": 0,
          "filename_prefix": "comfyUI",
          "format": "video/h264-mp4",
          "pix_fmt": "yuv420p",
          "crf": 20,
          "save_metadata": True,
          "pingpong": False,
          "save_output": True,
          "images": [
            "14",
            0
          ]
        },
        "class_type": "VHS_VideoCombine",
        "_meta": {
          "title": "合并为视频"
        }
      }
    }
  3. 订阅结果。

    1. 执行以下命令安装eas_prediction SDK。

      pip install eas_prediction  --user
    2. 执行以下代码,获取返回结果。

      from eas_prediction import QueueClient
      
      sink_queue = QueueClient('<service_domain>', '<service_name>/sink')
      sink_queue.set_token('<token>')
      sink_queue.init()
      
      watcher = sink_queue.watch(0, 5, auto_commit=False)
      for x in watcher.run():
          if 'task_id' in x.tags:
              print('index {} task_id is {}'.format(x.index, x.tags['task_id']))
          print(f'index {x.index} data is {x.data}')
          sink_queue.commit(x.index)
      

      其中关键配置说明如下:

      配置项

      描述

      <service_domain>

      请替换为步骤1查询的调用信息。例如139699392458****.cn-hangzhou.pai-eas.aliyuncs.com

      <service_name>

      请替换为EAS服务名称。

      <token>

      请替换为步骤1查询的Token。

      返回结果示例如下:

      index 42 task_id is txt2img_0
      index 42 data is b'[{"type": "executed", "data": {"node": "9", "output": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"type": "executed", "data": {"node": "15", "output": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"type": "executed", "data": {"node": "19", "output": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 43 task_id is txt2img_1
      index 43 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 44 task_id is txt2img_2
      index 44 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 45 task_id is txt2img_3
      index 45 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 46 task_id is txt2img_4
      index 46 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'

      您可以在挂载存储的output目录中,查看推理结果文件。

附录

如何生成请求体

您需要在WebUI页面设置满足业务需求的工作流,然后构建相应的请求体。具体操作步骤如下:

  1. 模型在线服务页面,单击目标服务的服务方式列下的查看Web应用,进入ComfyUI WebUI页面。

  2. 在WebUI页面,单击设置按钮,并在Settings对话框中选中启用开发模式选项复选框。image

  3. 在WebUI页面,根据您的业务需求配置工作流。

    您可以在Checkpoint加载器(Load Checkpoint)区域选择模型,在CLIP文本编码器(CLIP Text Encode)中输入正向和反向提示词、调整采样器配置等。完成这些操作后,单击提示词队列(Queue Prompt)以获取AI生成的视频,并确保所有配置都符合您的需求。

  4. 确定工作流符合预期后,请单击保存(API格式),下载该工作流对应的JSON文件。image

    调用API的请求体需要将下载的JSON文件内容置于prompt键下进行包装。例如,上述工作流对应的请求体为:

    {
        "prompt": {
            "3": {
                "inputs": {
                    "seed": 367490676387803,
                    "steps": 40,
                    "cfg": 7,
                    "sampler_name": "dpmpp_sde_gpu",
                    "scheduler": "karras",
                    "denoise": 1,
                    "model": [
                        "4",
                        0
                    ],
                    "positive": [
                        "6",
                        0
                    ],
                    "negative": [
                        "7",
                        0
                    ],
                    "latent_image": [
                        "5",
                        0
                    ]
                },
                "class_type": "KSampler",
                "_meta": {
                    "title": "K采样器"
                }
            },
            "4": {
                "inputs": {
                    "ckpt_name": "LandscapeBING_v10.safetensors"
                },
                "class_type": "CheckpointLoaderSimple",
                "_meta": {
                    "title": "Checkpoint加载器(简易)"
                }
            },
            "5": {
                "inputs": {
                    "width": 720,
                    "height": 1280,
                    "batch_size": 1
                },
                "class_type": "EmptyLatentImage",
                "_meta": {
                    "title": "空Latent"
                }
            },
            "6": {
                "inputs": {
                    "text": "Rocket takes off from the ground, fire,sky, airplane",
                    "clip": [
                        "4",
                        1
                    ]
                },
                "class_type": "CLIPTextEncode",
                "_meta": {
                    "title": "CLIP文本编码器"
                }
            },
            "7": {
                "inputs": {
                    "text": "",
                    "clip": [
                        "4",
                        1
                    ]
                },
                "class_type": "CLIPTextEncode",
                "_meta": {
                    "title": "CLIP文本编码器"
                }
            },
            "8": {
                "inputs": {
                    "samples": [
                        "3",
                        0
                    ],
                    "vae": [
                        "4",
                        2
                    ]
                },
                "class_type": "VAEDecode",
                "_meta": {
                    "title": "VAE解码"
                }
            },
            "9": {
                "inputs": {
                    "filename_prefix": "ComfyUI",
                    "images": [
                        "8",
                        0
                    ]
                },
                "class_type": "SaveImage",
                "_meta": {
                    "title": "保存图像"
                }
            },
            "13": {
                "inputs": {
                    "seed": 510424455529432,
                    "steps": 40,
                    "cfg": 2.5,
                    "sampler_name": "euler_ancestral",
                    "scheduler": "karras",
                    "denoise": 1,
                    "model": [
                        "17",
                        0
                    ],
                    "positive": [
                        "16",
                        0
                    ],
                    "negative": [
                        "16",
                        1
                    ],
                    "latent_image": [
                        "16",
                        2
                    ]
                },
                "class_type": "KSampler",
                "_meta": {
                    "title": "K采样器"
                }
            },
            "14": {
                "inputs": {
                    "samples": [
                        "13",
                        0
                    ],
                    "vae": [
                        "18",
                        2
                    ]
                },
                "class_type": "VAEDecode",
                "_meta": {
                    "title": "VAE解码"
                }
            },
            "15": {
                "inputs": {
                    "filename_prefix": "ComfyUI",
                    "fps": 10,
                    "lossless": false,
                    "quality": 85,
                    "method": "default",
                    "images": [
                        "14",
                        0
                    ]
                },
                "class_type": "SaveAnimatedWEBP",
                "_meta": {
                    "title": "保存WEBP"
                }
            },
            "16": {
                "inputs": {
                    "width": 512,
                    "height": 768,
                    "video_frames": 35,
                    "motion_bucket_id": 140,
                    "fps": 15,
                    "augmentation_level": 0.15,
                    "clip_vision": [
                        "18",
                        1
                    ],
                    "init_image": [
                        "8",
                        0
                    ],
                    "vae": [
                        "18",
                        2
                    ]
                },
                "class_type": "SVD_img2vid_Conditioning",
                "_meta": {
                    "title": "SVD_图像到视频_条件"
                }
            },
            "17": {
                "inputs": {
                    "min_cfg": 1,
                    "model": [
                        "18",
                        0
                    ]
                },
                "class_type": "VideoLinearCFGGuidance",
                "_meta": {
                    "title": "线性CFG引导"
                }
            },
            "18": {
                "inputs": {
                    "ckpt_name": "svd_xt_image_decoder.safetensors"
                },
                "class_type": "ImageOnlyCheckpointLoader",
                "_meta": {
                    "title": "Checkpoint加载器(仅图像)"
                }
            },
            "19": {
                "inputs": {
                    "frame_rate": 10,
                    "loop_count": 0,
                    "filename_prefix": "comfyUI",
                    "format": "video/h264-mp4",
                    "pix_fmt": "yuv420p",
                    "crf": 20,
                    "save_metadata": true,
                    "pingpong": false,
                    "save_output": true,
                    "images": [
                        "14",
                        0
                    ]
                },
                "class_type": "VHS_VideoCombine",
                "_meta": {
                    "title": "合并为视频"
                }
            }
        }
    }

集群版服务原理介绍

实现原理图如下:

image
  • 集群版服务主要针对多用户场景,实现了客户端和后端推理实例解耦,以便多用户可以分时复用后端推理实例,提升实例的利用率和降低推理成本。

  • Proxy代理主要负责客户端进程和推理实例的管理。用户的所有操作都在自己的进程中进行处理,相关的文件操作仅限于公共目录和个人目录,从而实现了用户间工作目录的有效隔离。当用户需要使用推理实例来处理请求时,Proxy代理会从后端推理实例中找到可用的空闲实例来处理该推理请求。

加速图片生成速度

xFormers是基于Transformer的开源加速工具,能够有效缩短图片和视频生成时长,节省显存使用。ComfyUI镜像部署默认已开启xFormers加速。

  • 本页导读
文档反馈