Desung commited on
Commit
0caa6c4
·
verified ·
1 Parent(s): f7e3fc6

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -400
app.py DELETED
@@ -1,400 +0,0 @@
1
- # Copyright 2025 lishiyan
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # https://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import gradio as gr
16
- import requests
17
- import json
18
- import os
19
- import base64
20
- import tempfile
21
- from urllib.parse import urlparse
22
- from datetime import datetime
23
- from PIL import Image
24
- import io
25
-
26
- # API配置
27
- API_KEY = "ns19uq2DpPZ9fuaZunfFmodmmdGrh4Erqz7G9ykeosOrPrzoZ0sVwQdeX4Mx0VWG"
28
- API_URL = "https://api-staging.zhipuai-infra.cn/v1/images/edits"
29
-
30
- def get_image_base64(image_path):
31
- """将本地图片文件转换为 base64 字符串"""
32
- try:
33
- with open(image_path, 'rb') as image_file:
34
- image_data = image_file.read()
35
- base64_str = base64.b64encode(image_data).decode('utf-8')
36
- return base64_str
37
- except Exception as e:
38
- print(f"转换图片为 base64 时出错: {e}")
39
- return None
40
-
41
- def pil_to_base64(pil_image):
42
- """将PIL图像转换为base64字符串"""
43
- try:
44
- # 转换为RGB模式(如果需要)
45
- if pil_image.mode != 'RGB':
46
- pil_image = pil_image.convert('RGB')
47
-
48
- # 保存到内存中的字节流
49
- buffer = io.BytesIO()
50
- pil_image.save(buffer, format='PNG')
51
- buffer.seek(0)
52
-
53
- # 转换为base64
54
- image_data = buffer.getvalue()
55
- base64_str = base64.b64encode(image_data).decode('utf-8')
56
- return base64_str
57
- except Exception as e:
58
- print(f"转换PIL图像为 base64 时出错: {e}")
59
- return None
60
-
61
- def resize_image_to_ratio(pil_image, target_ratio):
62
- """调整PIL图片到指定比例"""
63
- try:
64
- # 解析目标比例
65
- ratio_parts = target_ratio.split(':')
66
- if len(ratio_parts) != 2:
67
- return None
68
-
69
- target_width_ratio = float(ratio_parts[0])
70
- target_height_ratio = float(ratio_parts[1])
71
- target_aspect_ratio = target_width_ratio / target_height_ratio
72
-
73
- original_width, original_height = pil_image.size
74
- original_aspect_ratio = original_width / original_height
75
-
76
- # 如果比例已经匹配,直接返回原图
77
- if abs(original_aspect_ratio - target_aspect_ratio) < 0.01:
78
- return pil_image
79
-
80
- # 计算新的尺寸
81
- if target_aspect_ratio > original_aspect_ratio:
82
- # 目标比例更宽,以宽度为准
83
- new_width = original_width
84
- new_height = int(original_width / target_aspect_ratio)
85
- else:
86
- # 目标比例更高,以高度为准
87
- new_height = original_height
88
- new_width = int(original_height * target_aspect_ratio)
89
-
90
- # 调整图片尺寸
91
- resized_img = pil_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
92
- return resized_img
93
-
94
- except Exception as e:
95
- print(f"调整图片比例时出错: {e}")
96
- return None
97
-
98
- def get_template_image(target_ratio):
99
- """根据比例获取模板图片"""
100
- # 本地input目录路径
101
- input_dir = "image_editor/input"
102
-
103
- # 预定义的比例映射到不同的图片文件
104
- ratio_mapping = {
105
- "1:1": "1-1.png",
106
- "16:9": "16-9.png",
107
- "9:16": "9-16.png",
108
- "3:4": "3-4.png",
109
- "4:3": "4-3.png"
110
- }
111
-
112
- if target_ratio in ratio_mapping:
113
- image_path = os.path.join(input_dir, ratio_mapping[target_ratio])
114
- if os.path.exists(image_path):
115
- try:
116
- return Image.open(image_path)
117
- except Exception as e:
118
- print(f"打开模板图片失败: {e}")
119
- return None
120
-
121
- # 如果没有找到对应的模板,返回默认的1:1图片
122
- default_path = os.path.join(input_dir, ratio_mapping["1:1"])
123
- if os.path.exists(default_path):
124
- try:
125
- return Image.open(default_path)
126
- except Exception as e:
127
- print(f"打开默认模板图片失败: {e}")
128
- return None
129
-
130
- return None
131
-
132
- def edit_image_with_zhipu_api(prompt, image_base64):
133
- """使用智谱AI图像编辑API编辑图片"""
134
- headers = {
135
- 'Authorization': f'Bearer {API_KEY}',
136
- 'Accept': 'application/json',
137
- 'Content-Type': 'application/json'
138
- }
139
-
140
- data = {
141
- "model": "toc-qwen-image-edit-stage",
142
- "prompt": prompt,
143
- "image_url": image_base64
144
- }
145
-
146
- try:
147
- response = requests.post(API_URL, headers=headers, json=data)
148
- response.raise_for_status()
149
- return response.json()
150
- except requests.exceptions.RequestException as e:
151
- print(f"请求错误: {e}")
152
- return None
153
- except json.JSONDecodeError as e:
154
- print(f"JSON解析错误: {e}")
155
- return None
156
-
157
- def download_image_from_url(image_url):
158
- """从URL下载图片并返回PIL图像"""
159
- try:
160
- response = requests.get(image_url, stream=True)
161
- response.raise_for_status()
162
-
163
- # 将响应内容转换为PIL图像
164
- image_data = response.content
165
- pil_image = Image.open(io.BytesIO(image_data))
166
- return pil_image
167
-
168
- except Exception as e:
169
- print(f"下载图片时出错: {e}")
170
- return None
171
-
172
- def save_image_to_output(pil_image, prefix="edited"):
173
- """保存PIL图像到output目录"""
174
- try:
175
- # 创建输出目录
176
- output_dir = "output"
177
- if not os.path.exists(output_dir):
178
- os.makedirs(output_dir)
179
-
180
- # 生成文件名
181
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
182
- filename = f"{prefix}_{timestamp}.png"
183
- save_path = os.path.join(output_dir, filename)
184
-
185
- # 保存图片
186
- pil_image.save(save_path, 'PNG')
187
- print(f"图片已保存到: {save_path}")
188
- return save_path
189
-
190
- except Exception as e:
191
- print(f"保存图片时出错: {e}")
192
- return None
193
-
194
- def img2img_edit(input_image, prompt, target_ratio, resize_option):
195
- """图生图功能"""
196
- if input_image is None:
197
- return None, "请上传一张图片"
198
-
199
- if not prompt.strip():
200
- return None, "请输入编辑提示词"
201
-
202
- try:
203
- # 处理图片
204
- processed_image = input_image
205
-
206
- # 根据用户选择调整图片比例
207
- if resize_option == "调整到指定比例" and target_ratio != "保持原比例":
208
- processed_image = resize_image_to_ratio(input_image, target_ratio)
209
- if processed_image is None:
210
- return None, "图片比例调整失败"
211
-
212
- # 转换为base64
213
- image_base64 = pil_to_base64(processed_image)
214
- if not image_base64:
215
- return None, "图片转换失败"
216
-
217
- # 调用API
218
- result = edit_image_with_zhipu_api(prompt, image_base64)
219
-
220
- if result and 'data' in result and len(result['data']) > 0:
221
- # 获取第一张编辑后的图片
222
- if 'url' in result['data'][0]:
223
- edited_image_url = result['data'][0]['url']
224
- edited_image = download_image_from_url(edited_image_url)
225
-
226
- if edited_image:
227
- # 保存到本地
228
- save_image_to_output(edited_image, "img2img")
229
- return edited_image, "图片编辑成功!"
230
- else:
231
- return None, "下载编辑后的图片失败"
232
- else:
233
- return None, "API响应中没有找到图片URL"
234
- else:
235
- return None, "API调用失败或返回结果为空"
236
-
237
- except Exception as e:
238
- return None, f"处理过程中出错: {str(e)}"
239
-
240
- def text2img_edit(prompt, target_ratio):
241
- """文本到图像编辑功能(基于模板)"""
242
- if not prompt.strip():
243
- return None, "请输入编辑提示词"
244
-
245
- try:
246
- # 获取模板图片
247
- template_image = get_template_image(target_ratio)
248
- if template_image is None:
249
- return None, f"无法获取比例为 {target_ratio} 的模板图片"
250
-
251
- # 转换为base64
252
- image_base64 = pil_to_base64(template_image)
253
- if not image_base64:
254
- return None, "模板图片转换失败"
255
-
256
- # 调用API
257
- result = edit_image_with_zhipu_api(prompt, image_base64)
258
-
259
- if result and 'data' in result and len(result['data']) > 0:
260
- # 获取第一张编辑后的图片
261
- if 'url' in result['data'][0]:
262
- edited_image_url = result['data'][0]['url']
263
- edited_image = download_image_from_url(edited_image_url)
264
-
265
- if edited_image:
266
- # 保存到本地
267
- save_image_to_output(edited_image, f"text2img_{target_ratio.replace(':', '-')}")
268
- return edited_image, f"基于 {target_ratio} 模板的图片编辑成功!"
269
- else:
270
- return None, "下载编辑后的图片失败"
271
- else:
272
- return None, "API响应中没有找到图片URL"
273
- else:
274
- return None, "API调用失败或返回结果为空"
275
-
276
- except Exception as e:
277
- return None, f"处理过程中出错: {str(e)}"
278
-
279
- # 创建Gradio界面
280
- def create_interface():
281
- with gr.Blocks(title="智谱AI图像编辑工具", theme=gr.themes.Soft()) as demo:
282
- gr.Markdown("# 🎨 智谱AI图像编辑工具")
283
- gr.Markdown("基于智谱AI的图像编辑功能,支持图生图��基于模板的文本到图像编辑")
284
-
285
- with gr.Tabs():
286
- # 图生图选项卡
287
- with gr.TabItem("📸 图生图"):
288
- gr.Markdown("### 上传图片并根据提示词进行编辑")
289
-
290
- with gr.Row():
291
- with gr.Column():
292
- img2img_input = gr.Image(
293
- label="上传图片",
294
- type="pil",
295
- height=400
296
- )
297
- img2img_prompt = gr.Textbox(
298
- label="编辑提示词",
299
- placeholder="描述你想要的编辑效果,例如:将背景改为海滩",
300
- lines=3
301
- )
302
-
303
- with gr.Row():
304
- img2img_resize = gr.Radio(
305
- choices=["保持原比例", "调整到指定比例"],
306
- value="保持原比例",
307
- label="比例选项"
308
- )
309
- img2img_ratio = gr.Dropdown(
310
- choices=["1:1", "16:9", "9:16", "4:3", "3:4"],
311
- value="1:1",
312
- label="目标比例",
313
- visible=False
314
- )
315
-
316
- img2img_btn = gr.Button("🎨 开始编辑", variant="primary", size="lg")
317
-
318
- with gr.Column():
319
- img2img_output = gr.Image(label="编辑结果", height=400)
320
- img2img_status = gr.Textbox(label="状态信息", interactive=False)
321
-
322
- # 显示/隐藏比例选择
323
- def toggle_ratio_visibility(resize_option):
324
- return gr.update(visible=(resize_option == "调整到指定比例"))
325
-
326
- img2img_resize.change(
327
- toggle_ratio_visibility,
328
- inputs=[img2img_resize],
329
- outputs=[img2img_ratio]
330
- )
331
-
332
- # 绑定编辑功能
333
- img2img_btn.click(
334
- img2img_edit,
335
- inputs=[img2img_input, img2img_prompt, img2img_ratio, img2img_resize],
336
- outputs=[img2img_output, img2img_status]
337
- )
338
-
339
- # 文本到图像编辑选项卡
340
- with gr.TabItem("📝 文生图"):
341
- gr.Markdown("### 基于预设模板图片进行编辑")
342
-
343
- with gr.Row():
344
- with gr.Column():
345
- text2img_prompt = gr.Textbox(
346
- label="编辑提示词",
347
- placeholder="描述你想要生成的图像,例如:一只可爱的小猫在花园里",
348
- lines=4
349
- )
350
- text2img_ratio = gr.Dropdown(
351
- choices=["1:1", "16:9", "9:16", "4:3", "3:4"],
352
- value="1:1",
353
- label="选择模板比例"
354
- )
355
- text2img_btn = gr.Button("🎨 开始编辑", variant="primary", size="lg")
356
-
357
- with gr.Column():
358
- text2img_output = gr.Image(label="编辑结果", height=400)
359
- text2img_status = gr.Textbox(label="状态信息", interactive=False)
360
-
361
- # 绑定编辑功能
362
- text2img_btn.click(
363
- text2img_edit,
364
- inputs=[text2img_prompt, text2img_ratio],
365
- outputs=[text2img_output, text2img_status]
366
- )
367
-
368
- # 添加说明信息
369
- with gr.Accordion("📋 使用说明", open=False):
370
- gr.Markdown("""
371
- ### 功能说明:
372
-
373
- **图生图:**
374
- - 上传任意图片
375
- - 输入编辑提示词描述想要的效果
376
- - 可选择保持原比例或调整到指定比例
377
- - 支持的比例:1:1(正方形)、16:9(宽屏)、9:16(竖屏)、4:3(传统)、3:4(竖版传统)
378
-
379
- **文生图:**
380
- - 基于预设的不同比例模板图片进行编辑
381
- - 输入提示词描述想要生成的内容
382
- - 选择合适的输出比例
383
-
384
- ### 注意事项:
385
- - 编辑后的图片会自动保存到 `output` 目录
386
- - 请确保提示词描述清晰具体
387
- - API调用可能需要一些时间,请耐心等待
388
- """)
389
-
390
- return demo
391
-
392
- if __name__ == "__main__":
393
- # 创建并启动界面
394
- demo = create_interface()
395
- demo.launch(
396
- server_name="0.0.0.0",
397
- server_port=7861,
398
- share=False,
399
- show_error=True
400
- )