commit JimengI2I_API node

This commit is contained in:
chflame163
2025-08-09 20:50:26 +08:00
parent 0affa0ccac
commit d2093fe100
7 changed files with 283 additions and 2 deletions
+15
View File
@@ -144,6 +144,7 @@ Please try downgrading the ```protobuf``` dependency package to 3.20.3, or set e
**If the dependency package error after updating, please double clicking ```repair_dependency.bat``` (for Official ComfyUI Protable) or ```repair_dependency_aki.bat``` (for ComfyUI-aki-v1.x) in the plugin folder to reinstall the dependency packages.
* Commit [JimengImageToImageAPI](#JimengImageToImageAPI) node, edit images using the Instant Dreaming Image 3.0 API. Create an account on [Volcano Engine](#https://console.volcengine.com/iam/keymanage) and apply for API AccessKeyID and SecretAccessKey. Fill them into the ```api_key.ini``` directory in the plugin directory.
* Commit [SAM2UltraV2](SAM2UltraV2) and [LoadSAM2Model](LoadSAM2Model) nodes, Change the SAM model to an external input to save resources when using multiple nodes.
* Commit [JoyCaptionBetaOne](JoyCaptionBetaOne), [LoadJoyCaptionBeta1Model](LoadJoyCaptionBeta1Model), [JoyCaptionBeta1ExtraOptions](JoyCaptionBeta1ExtraOptions) nodes, Generate prompt words using the JoyCaption Beta One model.
* Commit [SaveImagePLusV2](SaveImagePlusV2) node, add custom file names and setting up the dpi of image.
@@ -563,6 +564,20 @@ Node Options:
* dtype: The model accuracy has two options: bf16 and fp32.
* device: The model loading device has two options: cuda or cpu.
### <a id="table1">JimengImageToImageAPI</a>
Edit images using the Jimeng API.
Please create an account on [Volcano Engine](#https://console.volcengine.com/iam/keymanage), apply for API AccessKeyID and SecretAccessKey, and fill them in ```api_key. ini```. This file is located in the root directory of the plugin and its default name is ```api_key.ini.example```. When using this file for the first time, you need to change the file extension to '.ini'. Open with text editing software and fill in the corresponding values after ```volcengine_SecretAccessKey=``` and ```volcengine_SecretAccessKey=```.
![image](image/jimeng_image_to_image_api_example.jpg)
Node Options:
![image](image/jimeng_image_to_image_api_node.jpg)
* image: The input image.
* model: Choose the DreamMap and Life Map model. Currently, only the Jimeng_i2i-v30 model is supported.
* time_out: The maximum time limit for waiting for API return, in seconds. If this time is exceeded, the node will end running.
* scale: The scale parameter of jimeng_i2iuv30 is set to 0.5 by default.
* seed: The seed value.
* prompt: The prompt.
### <a id="table1">UserPromptGeneratorTxtImg</a>
+15
View File
@@ -121,6 +121,7 @@ If this call came from a _pb2.py file, your generated code is out of date and mu
## 更新说明
**如果本插件更新后出现依赖包错误,请双击运行插件目录下的```install_requirements.bat```(官方便携包),或 ```install_requirements_aki.bat```(秋叶整合包) 重新安装依赖包。
* 添加 [JimengImageToImageAPI](#JimengImageToImageAPI) 节点,使用即梦图生图3.0API对图片进行编辑。在[火山引擎](#https://console.volcengine.com/iam/keymanage) 创建账号,并申请API AccessKeyID 和 SecretAccessKey,将其填入插件目录下的```api_key.ini```。
* 添加 [SAM2UltraV2](SAM2UltraV2) 和 [LoadSAM2Model](LoadSAM2Model) 节点,将SAM模型改为外部输入,在使用多个节点时节省资源。
* 添加 [JoyCaptionBetaOne](JoyCaptionBetaOne), [LoadJoyCaptionBeta1Model](LoadJoyCaptionBeta1Model), [JoyCaptionBeta1ExtraOptions](JoyCaptionBeta1ExtraOptions) 节点,使用JoyCaption-Beta-One模型生成提示词。
* 添加 [SaveImagePLusV2](SaveImagePlusV2) 节点,增加自定义文件名和设置dpi。
@@ -539,6 +540,20 @@ JoyCaption Beta One的extra_options参数节点。
* dtype: 模型精度,有bf16和fp32两个选项。
* device: 模型加载设备,有cuda和cpu两个选项。
### <a id="table1">JimengImageToImageAPI</a>
使用即梦图生图3.0API对图片进行编辑。
请在[火山引擎](#https://console.volcengine.com/iam/keymanage) 创建账号,并申请API AccessKeyID 和 SecretAccessKey,并将其填到```api_key.ini```。 这个文件位于插件根目录下, 默认名字是```api_key.ini.example```, 初次使用这个文件需将文件后缀改为.ini。用文本编辑软件打开,在```volcengine_AccessKeyId=``` 和 ```volcengine_SecretAccessKey=```后面填入对应的值。
![image](image/jimeng_image_to_image_api_example.jpg)
节点选项说明:
![image](image/jimeng_image_to_image_api_node.jpg)
* image: 图片输入。
* model: 选择即梦图生图模型。目前仅支持 jimeng_i2i_v30 模型。
* time_out: 等待API返回的时间上限,单位为秒,超出此时间将结束节点运行。
* scale: jimeng_i2i_v30的scale参数,默认为0.5。
* seed: 种子值。
* prompt: 提示词。
### <a id="table1">UserPromptGeneratorTxtImg</a>
用于生成SD文本到图片提示词的UserPrompt预设。
+3 -1
View File
@@ -2,5 +2,7 @@
google_api_key=
zhipu_api_key=
deepseek_api_key=
volcengine_api_key=
aliyun_api_key=
volcengine_api_key=
volcengine_AccessKeyId=
volcengine_SecretAccessKey=
Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

+249
View File
@@ -0,0 +1,249 @@
import json
import sys
import os
import io
import base64
import datetime
import hashlib
import hmac
import requests
import time
from PIL import Image
import torch
from .imagefunc import tensor2pil, pil2tensor, log, get_api_key, fit_resize_image
jimeng_i2i_model_list = ["jimeng_i2i_v30"]
jimeng_t2i_model_list = ["jimeng_high_aes_general_v21_L"]
method = 'POST'
host = 'visual.volcengineapi.com'
region = 'cn-north-1'
endpoint = 'https://visual.volcengineapi.com'
service = 'cv'
image_max_side_length = 4096
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(key.encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'request')
return kSigning
def formatQuery(parameters):
request_parameters_init = ''
for key in sorted(parameters):
request_parameters_init += key + '=' + parameters[key] + '&'
request_parameters = request_parameters_init[:-1]
return request_parameters
def signV4Request(access_key, secret_key, service, req_query, req_body):
if access_key is None or secret_key is None:
print('No access key is available.')
sys.exit()
t = datetime.datetime.utcnow()
current_date = t.strftime('%Y%m%dT%H%M%SZ')
# current_date = '20210818T095729Z'
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
canonical_uri = '/'
canonical_querystring = req_query
signed_headers = 'content-type;host;x-content-sha256;x-date'
payload_hash = hashlib.sha256(req_body.encode('utf-8')).hexdigest()
content_type = 'application/json'
canonical_headers = 'content-type:' + content_type + '\n' + 'host:' + host + \
'\n' + 'x-content-sha256:' + payload_hash + \
'\n' + 'x-date:' + current_date + '\n'
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + \
'\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
# print(canonical_request)
algorithm = 'HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'request'
string_to_sign = algorithm + '\n' + current_date + '\n' + credential_scope + '\n' + hashlib.sha256(
canonical_request.encode('utf-8')).hexdigest()
# print(string_to_sign)
signing_key = getSignatureKey(secret_key, datestamp, region, service)
# print(signing_key)
signature = hmac.new(signing_key, (string_to_sign).encode(
'utf-8'), hashlib.sha256).hexdigest()
# print(signature)
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + \
credential_scope + ', ' + 'SignedHeaders=' + \
signed_headers + ', ' + 'Signature=' + signature
# print(authorization_header)
headers = {'X-Date': current_date,
'Authorization': authorization_header,
'X-Content-Sha256': payload_hash,
'Content-Type': content_type
}
# print(headers)
# ************* SEND THE REQUEST *************
request_url = endpoint + '?' + canonical_querystring
# print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
# print('Request URL = ' + request_url)
try:
r = requests.post(request_url, headers=headers, data=req_body)
except Exception as err:
print(f'error occurred: {err}')
raise
else:
# print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
# print(f'Response code: {r.status_code}\n')
# 使用 replace 方法将 \u0026 替换为 &
resp_str = r.text.replace("\\u0026", "&")
# print(f'Response body: {resp_str}\n')
return json.loads(resp_str)
def round_to_multiple_of_16(x):
return int(round(x / 16)) * 16
def get_closest_api_size(orig_width, orig_height):
"""
根据原始图片尺寸,返回最接近的API尺寸。
"""
aspect = orig_width / orig_height
max_width, max_height = 2016, 1536
max_aspect = max_width / max_height
if aspect > max_aspect:
# 限制宽度
width = max_width
height = width / aspect
else:
# 限制高度
height = max_height
width = aspect * height
# 四舍五入到16的倍数
width = round_to_multiple_of_16(width)
height = round_to_multiple_of_16(height)
# 再次确保合法范围
width = min(max(width, 512), 2016)
height = min(max(height, 512), 1536)
return width, height
class LS_Jimeng_i2i_API:
def __init__(self):
self.NODE_NAME = 'Jimeng Image2Image API'
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE",),
"model": (jimeng_i2i_model_list,),
"time_out": ("INT", {"default": 300, "min": 1, "max": 3600, "step": 1}), # 300s = 5min
"scale": ("FLOAT", {"default": 0.5, "min": 0, "max": 1, "step": 0.1}),
"seed": ("INT", {"default": 0, "min": 0, "max": 1e18, "step": 1}),
"prompt": ("STRING", {"default": "", "multiline": True}),
},
"optional": {
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = 'run_jimeng_i2i_api'
CATEGORY = '😺dzNodes/LayerUtility'
def run_jimeng_i2i_api(self, image, model, time_out, scale, seed, prompt):
ret_images = []
access_key = get_api_key("volcengine_AccessKeyId")
secret_key = get_api_key("volcengine_SecretAccessKey")
# image shape = b, h, w, c
orig_width = image.shape[2]
orig_height = image.shape[1]
output_width, output_height = get_closest_api_size(orig_width, orig_height)
for img in image:
img = torch.unsqueeze(img, 0)
orig_image = tensor2pil(img).convert('RGB')
orig_image = fit_resize_image(orig_image, output_width, output_height, 'fill', Image.BICUBIC)
buffered = io.BytesIO()
orig_image.save(buffered, format="JPEG")
image_bytes = buffered.getvalue()
image_base64 = base64.b64encode(image_bytes).decode("utf-8")
body_params = {
"req_key": model,
"binary_data_base64": [image_base64],
"prompt": prompt,
"seed": seed,
"scale": scale,
"width": output_width,
"height": output_height,
}
formatted_body = json.dumps(body_params)
query_params = {
'Action': 'CVSync2AsyncSubmitTask',
'Version': '2022-08-31',
}
formatted_query = formatQuery(query_params)
# 发送请求
result = signV4Request(access_key, secret_key, service, formatQuery(query_params), formatted_body)
response_code = result["code"]
if response_code > 10000:
print(f"Error: {result}")
raise Exception(f"error code: {response_code}")
task_id = result["data"]["task_id"]
log(f"Send Request to Jimeng API: task_id = {task_id}")
# 获取结果
start_time = time.time()
check_interval = 1
check_timeout = 60
query_params["Action"] = 'CVSync2AsyncGetResult'
formatted_query = formatQuery(query_params)
body_params = {
"req_key": "jimeng_i2i_v30",
"task_id": task_id,
}
formatted_body = json.dumps(body_params)
while True:
time.sleep(check_interval)
result = signV4Request(access_key, secret_key, service, formatted_query, formatted_body)
query_status = result["data"]["status"]
if query_status == "done":
# print(f"query_status: {query_status}")
break
if time.time() - start_time > check_timeout:
raise Exception("Jimeng API Timeout")
end_time = time.time()
use_time = round(end_time - start_time, 2)
log(f"Jimeng API responded in {use_time} seconds.")
base64_str = result["data"]["binary_data_base64"][0]
image_data = base64.b64decode(base64_str)
ret_image = Image.open(io.BytesIO(image_data))
ret_images.append(pil2tensor(ret_image))
return (torch.cat(ret_images, dim=0),)
NODE_CLASS_MAPPINGS = {
"LayerUtility: JimengI2IAPI": LS_Jimeng_i2i_API,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerUtility: JimengI2IAPI": "LayerUtility: Jimeng Imgae to Image API (Advance)",
}
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui_layerstyle_advance"
description = "The nodes detached from ComfyUI Layer Style are mainly those with complex requirements for dependency packages."
version = "2.0.22"
version = "2.0.23"
license = { text = "MIT License" }
dependencies = ["numpy", "matplotlib", "scikit_image", "scikit_learn", "opencv-contrib-python", "pymatting", "timm", "blend_modes", "transformers", "diffusers", "loguru", "colour-science", "huggingface_hub", "segment_anything", "addict", "omegaconf", "yapf", "wget", "iopath", "mediapipe", "typer_config", "fastapi", "rich", "google-generativeai", "ultralytics", "transparent-background", "accelerate", "onnxruntime", "bitsandbytes", "peft", "protobuf", "hydra-core", "blind-watermark", "qrcode", "pyzbar", "psd-tools", "wandb", "zhipuai", "openai","google-genai"]