17
17
18
18
logger = logging .getLogger (__name__ )
19
19
20
- class VolcanoOutpaintingNode :
21
- @classmethod
22
- def INPUT_TYPES (s ):
23
- return {
24
- "required" : {
25
- "image" : ("IMAGE" ,),
26
- "mask" : ("MASK" ,),
27
- "ak" : ("STRING" , {"default" : "" }),
28
- "sk" : ("STRING" , {"default" : "" }),
29
- "seed" : ("INT" , {"default" : 42 , "min" : 0 , "max" : 999999 })
30
- }
31
- }
32
-
33
- RETURN_TYPES = ("IMAGE" ,)
34
- RETURN_NAMES = ("result_image" ,)
35
- FUNCTION = "process_outpainting"
36
- CATEGORY = "image/volcano"
37
-
20
+ class VolcanoBaseNode :
21
+ """火山引擎视觉服务基类"""
22
+
23
+ def __init__ (self ):
24
+ self .visual_service = VisualService ()
25
+
38
26
def save_config (self , config ):
27
+ """保存配置到文件"""
39
28
config_path = os .path .join (config_dir , 'volcano_config.yml' )
40
29
with open (config_path , 'w' , encoding = 'utf-8' ) as f :
41
30
yaml .dump (config , f , indent = 4 )
42
31
43
32
def load_config (self ):
33
+ """从文件加载配置"""
44
34
config_path = os .path .join (config_dir , 'volcano_config.yml' )
45
- with open (config_path , 'r' , encoding = 'utf-8' ) as f :
46
- config = yaml .load (f , Loader = yaml .FullLoader )
47
- return config
35
+ try :
36
+ with open (config_path , 'r' , encoding = 'utf-8' ) as f :
37
+ config = yaml .load (f , Loader = yaml .FullLoader )
38
+ return config
39
+ except FileNotFoundError :
40
+ return {}
48
41
49
42
def pil_to_base64 (self , pil_image ):
50
43
"""将PIL图像转换为base64字符串"""
51
44
buffer = io .BytesIO ()
52
45
pil_image .save (buffer , format = 'PNG' )
53
46
img_data = buffer .getvalue ()
54
47
return base64 .b64encode (img_data ).decode ('utf-8' )
55
-
56
- def request_valcanic_outpainting (self , req_key , ak , sk , image_base64s , seed = 42 , top = 0 , left = 0 , bottom = 0 , right = 0 ):
57
- """调用火山引擎图像扩展API"""
58
- visual_service = VisualService ()
59
-
48
+
49
+ def setup_credentials (self , ak , sk ):
50
+ """设置API凭证"""
60
51
if ak and sk :
61
52
self .save_config ({"ak" : ak , "sk" : sk })
62
53
else :
@@ -65,34 +56,49 @@ def request_valcanic_outpainting(self, req_key, ak, sk, image_base64s, seed=42,
65
56
sk = config .get ("sk" )
66
57
if not ak or not sk :
67
58
raise Exception ("volcano engine ak or sk not found" )
68
-
69
- visual_service .set_ak (ak )
70
- visual_service .set_sk (sk )
71
-
72
- # 请求参数
73
- form = {
74
- "req_key" : req_key ,
75
- "binary_data_base64" : image_base64s ,
76
- "top" : top ,
77
- "left" : left ,
78
- "bottom" : bottom ,
79
- "right" : right ,
80
- "seed" : seed
81
- }
82
-
83
- resp = visual_service .cv_process (form )
59
+
60
+ self .visual_service .set_ak (ak )
61
+ self .visual_service .set_sk (sk )
62
+ return ak , sk
63
+
64
+ def request_volcano_api (self , ak , sk , form_data ):
65
+ """通用的火山引擎API请求方法"""
66
+ # 设置凭证
67
+ self .setup_credentials (ak , sk )
68
+
69
+ # 发送请求
70
+ resp = self .visual_service .cv_process (form_data )
84
71
85
72
# 解码返回的图像
86
73
img_base64 = resp ['data' ]['binary_data_base64' ][0 ]
87
74
img_data = base64 .b64decode (img_base64 )
88
- # 转换为PIL图像
89
75
result_image = Image .open (io .BytesIO (img_data ))
76
+
77
+ # 删除图片base64,方便print
78
+ resp ['data' ]['binary_data_base64' ][0 ] = ""
79
+ logger .debug (f"volcano api response: { resp } " )
80
+
81
+ return result_image , resp
90
82
91
- # 删除图片base64, 方便print
92
- resp ['data' ]['binary_data_base64' ][0 ] = ""
83
+ class VolcanoOutpaintingNode (VolcanoBaseNode ):
84
+ """火山引擎图像扩展节点"""
85
+
86
+ @classmethod
87
+ def INPUT_TYPES (s ):
88
+ return {
89
+ "required" : {
90
+ "image" : ("IMAGE" ,),
91
+ "mask" : ("MASK" ,),
92
+ "ak" : ("STRING" , {"default" : "" }),
93
+ "sk" : ("STRING" , {"default" : "" }),
94
+ "seed" : ("INT" , {"default" : 42 , "min" : 0 , "max" : 999999 })
95
+ }
96
+ }
93
97
94
- logger .debug (f"volcano outpainting response: { resp } " )
95
- return result_image , resp
98
+ RETURN_TYPES = ("IMAGE" ,)
99
+ RETURN_NAMES = ("result_image" ,)
100
+ FUNCTION = "process_outpainting"
101
+ CATEGORY = "image/volcano"
96
102
97
103
def process_outpainting (self , image , mask , ak , sk , seed ):
98
104
try :
@@ -104,18 +110,23 @@ def process_outpainting(self, image, mask, ak, sk, seed):
104
110
if pil_mask .mode != 'L' :
105
111
pil_mask = pil_mask .convert ('L' )
106
112
107
- # 直接转换为base64
113
+ # 转换为base64
108
114
image_base64 = self .pil_to_base64 (pil_image )
109
115
mask_base64 = self .pil_to_base64 (pil_mask )
110
116
111
- # 调用API
112
- result_image , _ = self .request_valcanic_outpainting (
113
- req_key = "i2i_outpainting" ,
114
- ak = ak ,
115
- sk = sk ,
116
- image_base64s = [image_base64 , mask_base64 ],
117
- seed = seed
118
- )
117
+ # 构建请求参数
118
+ form_data = {
119
+ "req_key" : "i2i_outpainting" ,
120
+ "binary_data_base64" : [image_base64 , mask_base64 ],
121
+ "top" : 0 ,
122
+ "left" : 0 ,
123
+ "bottom" : 0 ,
124
+ "right" : 0 ,
125
+ "seed" : seed
126
+ }
127
+
128
+ # 调用基类的通用API请求方法
129
+ result_image , _ = self .request_volcano_api (ak , sk , form_data )
119
130
120
131
# 使用节点库的转换函数转换结果为tensor
121
132
result_tensor = pil2tensor (result_image )
@@ -127,11 +138,66 @@ def process_outpainting(self, image, mask, ak, sk, seed):
127
138
# 返回原图作为fallback
128
139
return (image ,)
129
140
141
+ class VolcanoImageEditNode (VolcanoBaseNode ):
142
+ """火山引擎图像编辑节点"""
143
+
144
+ @classmethod
145
+ def INPUT_TYPES (s ):
146
+ return {
147
+ "required" : {
148
+ "image" : ("IMAGE" ,),
149
+ "prompt" : ("STRING" , {"multiline" : True , "default" : "" }),
150
+ "ak" : ("STRING" , {"default" : "" }),
151
+ "sk" : ("STRING" , {"default" : "" }),
152
+ "seed" : ("INT" , {"default" : 42 , "min" : 0 , "max" : 0xffffffffffffffff }),
153
+ "scale" : ("FLOAT" , {"default" : 0.5 , "min" : 0.1 , "max" : 1.0 , "step" : 0.01 })
154
+ },
155
+ "optional" : {
156
+ }
157
+ }
158
+
159
+ RETURN_TYPES = ("IMAGE" ,)
160
+ RETURN_NAMES = ("edited_image" ,)
161
+ FUNCTION = "process_image_edit"
162
+ CATEGORY = "image/volcano"
163
+
164
+ def process_image_edit (self , image , prompt , ak , sk , seed , scale = 0.5 ):
165
+ try :
166
+ # 使用节点库的转换函数
167
+ pil_image = tensor2pil (image )
168
+
169
+ # 转换为base64
170
+ image_base64 = self .pil_to_base64 (pil_image )
171
+ binary_data = [image_base64 ]
172
+ # 构建请求参数
173
+ form_data = {
174
+ "req_key" : "byteedit_v2.0" ,
175
+ "binary_data_base64" : binary_data ,
176
+ "prompt" : prompt ,
177
+ "seed" : seed ,
178
+ "scale" : scale
179
+ }
180
+
181
+ # 调用基类的通用API请求方法
182
+ result_image , _ = self .request_volcano_api (ak , sk , form_data )
183
+
184
+ # 使用节点库的转换函数转换结果为tensor
185
+ result_tensor = pil2tensor (result_image )
186
+
187
+ return (result_tensor ,)
188
+
189
+ except Exception as e :
190
+ logger .exception (f"图像编辑处理失败: { e } " )
191
+ # 返回原图作为fallback
192
+ return (image ,)
193
+
130
194
# 节点映射
131
195
NODE_CLASS_MAPPINGS = {
132
- "VolcanoOutpaintingNode" : VolcanoOutpaintingNode
196
+ "VolcanoOutpaintingNode" : VolcanoOutpaintingNode ,
197
+ "VolcanoImageEditNode" : VolcanoImageEditNode
133
198
}
134
199
135
200
NODE_DISPLAY_NAME_MAPPINGS = {
136
- "VolcanoOutpaintingNode" : "volcano outpainting"
201
+ "VolcanoOutpaintingNode" : "volcano outpainting" ,
202
+ "VolcanoImageEditNode" : "volcano image edit"
137
203
}
0 commit comments