Peng-Wang commited on
Commit
959541f
·
0 Parent(s):

Start fresh from current state

Browse files
Files changed (8) hide show
  1. .gitignore +1 -0
  2. LICENSE +11 -0
  3. README.md +35 -0
  4. app.py +152 -0
  5. app_future.py +168 -0
  6. caller.py +124 -0
  7. requirements.txt +5 -0
  8. uni_test/test.jpg +0 -0
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ *pyc
LICENSE ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.
2
+
3
+ You are free to:
4
+ - Share — copy and redistribute the material in any medium or format
5
+ - Adapt — remix, transform, and build upon the material
6
+
7
+ Under the following terms:
8
+ - Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
9
+ - NonCommercial — You may not use the material for commercial purposes.
10
+
11
+ No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
README.md ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Seed-Edit-APP
2
+
3
+ This repository contains the code for the **Seed-Edit** application. Seed-Edit is designed to simplify and enhance editing processes using advanced algorithms and data-driven insights.
4
+
5
+ ## Overview
6
+
7
+ Seed-Edit provides an intuitive interface and integrates with state-of-the-art tools to streamline your editing experience. The app leverages robust models to offer various editing options tailored to user needs.
8
+
9
+ ## Additional Resources
10
+
11
+ For more information and trials, please visit our official webpage for more details: [Seed-Edit Webpage](https://team.doubao.com/seed-edit) and the **[Doubao](https://www.doubao.com/chat/)/[Dreamina](https://dreamina.capcut.com/ai-tool/image/generate) APPs**.
12
+
13
+
14
+ ## License
15
+
16
+ This project is licensed under the CC BY-NC License. See `LICENSE` for more information.
17
+
18
+ ## Safety
19
+
20
+ <font size=2>
21
+ We strongly advise users not to knowingly generate or allow others to knowingly generate harmful content,
22
+ including hate, violence, pornography, deception, etc.
23
+ (注:本演示受CC BY-NC的许可协议限制。我们强烈建议,用户不应传播及不应允许他人传播以下内容,包括但不限于仇恨、暴力、色情、欺诈相关的有害信息。)
24
+
25
+ ---
26
+ title: Seed-Edit
27
+ emoji: 🌖
28
+ colorFrom: blue
29
+ colorTo: red
30
+ sdk: gradio
31
+ sdk_version: 4.44.1
32
+ app_file: app.py
33
+ pinned: false
34
+ license: other
35
+ ---
app.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (2024) Bytedance Ltd. and/or its affiliates
2
+ #
3
+ # This source code is licensed under the license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from __future__ import annotations
7
+ import os
8
+ import random
9
+ import uuid
10
+
11
+ import gradio as gr
12
+ import numpy as np
13
+
14
+ from loguru import logger
15
+ from caller import (
16
+ SeedT2ICaller,
17
+ SeedEditCaller
18
+ )
19
+ from PIL import Image
20
+
21
+ help_text = """
22
+ ## How to use this Demo
23
+ 1. Type in the caption/instruction text box, and click "Generate" to generate an initial image using Seed-T2I.
24
+ 2. Type in the caption/instruction text box, and click "Edit" to edit the current image using Seed-Edit.
25
+
26
+ This is a demo with limited QPS and a simple interface.
27
+ For a better experience, please use Doubao/Dreamina APP.
28
+
29
+ <font size=2>Note: This demo is governed by the license of CC BY-NC \
30
+ We strongly advise users not to knowingly generate or allow others to knowingly generate harmful content, \
31
+ including hate speech, violence, pornography, deception, etc. \
32
+ (注:本演示受CC BY-NC的许可协议限制。我们强烈建议,用户不应传播及不应允许他人传播以下内容,\
33
+ 包括但不限于仇恨言论、暴力、色情、欺诈相关的有害信息。)
34
+ """
35
+
36
+ example_instructions = [
37
+ "Make it a picasso painting",
38
+ "close its eye",
39
+ "convert to a bronze statue",
40
+ "make it wearing a hat",
41
+ "make it wearing a PhD suit",
42
+ "Turn it into an anime.",
43
+ "have it look like a graphic novel",
44
+ "make it gain weight",
45
+ "what would he look like bald?",
46
+ "Have it smile",
47
+ "Put in a cocktail party.",
48
+ "move to the beach.",
49
+ "add dramatic lighting",
50
+ "Convert to black and white",
51
+ "What if it were snowing?",
52
+ "Give a leather jacket",
53
+ "Turn into a cyborg!",
54
+ ]
55
+
56
+ def main():
57
+ resolution = 1024
58
+ cfg = {"resolution": resolution}
59
+ model_t2i = SeedT2ICaller(cfg)
60
+
61
+ cfg_edit = {}
62
+ model_edit = SeedEditCaller(cfg_edit)
63
+ logger.info("All models loaded")
64
+
65
+
66
+ def load_example():
67
+ example_image = Image.open(f"uni_test/test.jpg").convert("RGB")
68
+ example_instruction = random.choice(example_instructions)
69
+ edited_image, example_instruction = generate(example_image,
70
+ example_instruction,
71
+ cfg_scale=0.5)
72
+ return example_image, example_instruction, edited_image
73
+
74
+ def generate_t2i(instruction: str, cfg_scale: float = 0.5):
75
+ if not instruction:
76
+ return None, ""
77
+
78
+ logger.info("Generate images ...")
79
+ # Call model and capture the status
80
+ gen_image, success = model_t2i.generate(instruction, batch_size=1, cfg_scale=cfg_scale)
81
+ if not success or gen_image is None:
82
+ logger.error("Image generation failed or returned None. please retry")
83
+ return None, instruction
84
+ return gen_image, instruction
85
+
86
+ def generate(input_image: Image.Image, instruction: str = None, cfg_scale: float = 0.5):
87
+ logger.info("Generating images ...")
88
+ if not instruction or input_image is None:
89
+ return input_image, ""
90
+
91
+ logger.info("Running diffusion models ...")
92
+ edited_image, success = model_edit.edit(input_image, instruction, batch_size=1, cfg_scale=cfg_scale)
93
+ if not success or edited_image is None:
94
+ logger.error("Image editting failed or returned None.")
95
+ return None, instruction
96
+
97
+ return edited_image, instruction
98
+
99
+ def reset():
100
+ return None, None, ""
101
+
102
+ with gr.Blocks(css="footer {visibility: hidden}") as demo:
103
+ with gr.Row():
104
+ with gr.Column(scale=1, min_width=100):
105
+ generate_button = gr.Button("Generate")
106
+ with gr.Column(scale=1, min_width=100):
107
+ edit_button = gr.Button("Edit")
108
+ with gr.Column(scale=1, min_width=100):
109
+ load_button = gr.Button("Load Example")
110
+ with gr.Column(scale=1, min_width=100):
111
+ reset_button = gr.Button("Reset")
112
+
113
+ with gr.Row():
114
+ with gr.Column(scale=3):
115
+ instruction = gr.Textbox(lines=1, label="Edit/Caption Instruction", interactive=True, value=None)
116
+ with gr.Column(scale=1):
117
+ cfg_scale = gr.Slider(value=0.5, minimum=0.0, maximum=1.0, step=0.1, label="Edit/Text Strength (CFG)", interactive=True)
118
+
119
+ with gr.Row():
120
+ input_image = gr.Image(label="Input Image", type="pil", interactive=True,
121
+ height=resolution, width=resolution)
122
+ edited_image = gr.Image(label="Edited Image", type="pil",
123
+ interactive=False, height=resolution, width=resolution)
124
+
125
+ gr.Markdown(help_text)
126
+
127
+ load_button.click(
128
+ fn=load_example,
129
+ inputs=[],
130
+ outputs=[input_image, instruction, edited_image]
131
+ )
132
+ generate_button.click(
133
+ fn=generate_t2i,
134
+ inputs=[instruction, cfg_scale],
135
+ outputs=[input_image, instruction]
136
+ )
137
+ edit_button.click(
138
+ fn=generate,
139
+ inputs=[input_image, instruction, cfg_scale],
140
+ outputs=[edited_image, instruction]
141
+ )
142
+ reset_button.click(
143
+ fn=reset,
144
+ inputs=[],
145
+ outputs=[input_image, edited_image, instruction]
146
+ )
147
+
148
+ # demo.launch(server_name="0.0.0.0", server_port=8024)
149
+ demo.queue().launch(share=False)
150
+
151
+ if __name__ == "__main__":
152
+ main()
app_future.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (2024) Bytedance Ltd. and/or its affiliates
2
+ #
3
+ # This source code is licensed under the license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from __future__ import annotations
7
+ import uuid
8
+
9
+ from loguru import logger
10
+ import hashlib
11
+ import gradio as gr
12
+
13
+ import io
14
+ import base64
15
+
16
+ from caller import (
17
+ SeedT2ICaller,
18
+ SeedEditCaller
19
+ )
20
+ from PIL import Image
21
+
22
+ API_KEY = ""
23
+ help_text = """
24
+ ## How to use this Demo
25
+ 1. Type-in the caption/instruction text box, and click "Generate" to generate an initial image using Seed_T2I_V14 (CFG and steps are not used here)
26
+ 2. Type-in the caption/instruction text box, and click "Edit" to edit the current image.
27
+ 3. Click Undo if you are not satisfied with the current results, and re-edit. Otherwise, edit will apply to current results.
28
+ 4. Currently, we do not support too many rounds of editing [as shown in our video] since the current API hasn't been updated to the new model yet.
29
+
30
+ This is a demo with limited QPS and a simple interface.
31
+ For a better experience, please use Doubao/Dreamina APP.
32
+
33
+ <font size=2>Note: This demo is governed by the license of CC BY-NC \
34
+ We strongly advise users not to knowingly generate or allow others to knowingly generate harmful content, \
35
+ including hate speech, violence, pornography, deception, etc. \
36
+ (注:本演示受CC BY-NC的许可协议限制。我们强烈建议,用户不应传播及不应允许他人传播以下内容,\
37
+ 包括但不限于仇恨言论、暴力、色情、欺诈相关的有害信息。)
38
+ """
39
+
40
+ def image2str(image):
41
+ buf = io.BytesIO()
42
+ image.save(buf, format="PNG")
43
+ i_str = base64.b64encode(buf.getvalue()).decode()
44
+ return f'<div style="float:left"><img src="data:image/png;base64, {i_str}"></div>'
45
+
46
+ def main():
47
+ resolution = 1024
48
+ max_edit_iter = 3
49
+
50
+ cfg_t2i = {
51
+ "resolution": resolution
52
+ }
53
+ model_t2i = SeedT2ICaller(cfg_t2i)
54
+
55
+ cfg_edit = cfg_t2i
56
+
57
+ model_edit = SeedEditCaller(cfg_edit)
58
+ logger.info("All models loaded")
59
+
60
+ def generate_t2i(instruction: str, state):
61
+ logger.info("Generate images ...")
62
+ # 调用模型生成图像并捕获返回结果
63
+ gen_image, success = model_t2i.generate(instruction, batch_size=1)
64
+
65
+ # 检查生成是否成功以及生成的图像是否有效
66
+ if not success or gen_image is None:
67
+ logger.error("Image generation failed or returned None.")
68
+ raise ValueError("Image generation was unsuccessful.")
69
+
70
+ # Write cache
71
+ if state is None:
72
+ state = {}
73
+
74
+ output_md5 = hashlib.md5(gen_image.tobytes()).hexdigest()
75
+ logger.info(output_md5)
76
+ state[output_md5] = gen_image
77
+
78
+ return instruction, gen_image, state
79
+
80
+ def generate(prev_image, cur_image, cfg_scale, instruction, state):
81
+ if len(state.keys()) >= max_edit_iter:
82
+ return prev_image, cur_image, instruction, state
83
+
84
+ try:
85
+ if cur_image is None:
86
+ cur_image = prev_image
87
+
88
+ logger.info("Generating edited images ...")
89
+ if not instruction:
90
+ return prev_image, cur_image, instruction, state
91
+
92
+ logger.info("Running diffusion models ...")
93
+
94
+ image_out = f"./cache/{'-'.join(instruction.split()[:10])[:50]}_{uuid.uuid4()}.jpg"
95
+ logger.info(f"Input size {cur_image.size}")
96
+
97
+ edited_image, success = model_edit.edit(cur_image, instruction, batch_size=1, cfg_scale=cfg_scale, filename=image_out)
98
+ if not success or edited_image is None:
99
+ logger.error("Image generation failed or returned None.")
100
+ raise ValueError("Image generation was unsuccessful.")
101
+
102
+ output_md5 = hashlib.md5(edited_image.tobytes()).hexdigest()
103
+ logger.info(f"EDIT adding {output_md5}")
104
+ state[output_md5] = edited_image
105
+
106
+ return cur_image, edited_image, instruction, state
107
+
108
+ except Exception as e:
109
+ logger.error(e)
110
+ return prev_image, cur_image, instruction, state
111
+
112
+ def reset():
113
+ return 0.5, None, None, "", {}
114
+
115
+ def undo(prev_image, cur_image, instruction, state):
116
+ if cur_image is not None:
117
+ cur_md5 = hashlib.md5(cur_image.tobytes()).hexdigest()
118
+ if cur_md5 in state:
119
+ logger.info(f"UNDO removing {cur_md5}")
120
+ state.pop(cur_md5, None)
121
+ return prev_image, prev_image, instruction, state
122
+
123
+ def show_state(state):
124
+ num_cache = len(state.keys())
125
+ return f"Num Cache: {num_cache}" if num_cache < max_edit_iter else "Max edit number reached. Please reset for testing."
126
+
127
+ with gr.Blocks(css="footer {visibility: hidden}") as demo:
128
+ state = gr.State({})
129
+
130
+ with gr.Row():
131
+ with gr.Column(scale=2):
132
+ prev_image = gr.Image(label="Input Image", type="pil", interactive=True, visible=False, height=resolution, width=resolution)
133
+ cur_image = gr.Image(label="Edited Image", type="pil", interactive=True, height=resolution, width=resolution)
134
+
135
+ with gr.Column(scale=1):
136
+ with gr.Row():
137
+ generate_t2i_button = gr.Button("Generate")
138
+ generate_button = gr.Button("Edit")
139
+ reset_button = gr.Button("Reset")
140
+ undo_button = gr.Button("Undo")
141
+
142
+ with gr.Row():
143
+ instruction = gr.Textbox(lines=1, label="Caption (Generate) / Instruction (Edit)", interactive=True)
144
+
145
+ with gr.Row():
146
+ cfg_scale = gr.Slider(value=0.5, minimum=0.0, maximum=1.0, step=0.1, label="Edit Strength (CFG)", interactive=True)
147
+
148
+ with gr.Row():
149
+ output_label = gr.Label()
150
+
151
+ gr.Markdown(help_text)
152
+
153
+ # Function bindings
154
+ generate_t2i_button.click(generate_t2i, [instruction, state], [instruction, cur_image, state])
155
+ generate_button.click(generate, [prev_image, cur_image, cfg_scale, instruction, state], [prev_image, cur_image, instruction, state])
156
+ reset_button.click(reset, [], [cfg_scale, prev_image, cur_image, instruction, state])
157
+ undo_button.click(undo, [prev_image, cur_image, instruction, state], [prev_image, cur_image, instruction, state])
158
+
159
+ # Update state display
160
+ generate_t2i_button.click(show_state, [state], output_label)
161
+ generate_button.click(show_state, [state], output_label)
162
+ reset_button.click(show_state, [state], output_label)
163
+ undo_button.click(show_state, [state], output_label)
164
+
165
+ demo.launch(server_name="0.0.0.0", server_port=8024)
166
+
167
+ if __name__ == "__main__":
168
+ main()
caller.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (2024) Bytedance Ltd. and/or its affiliates
2
+ #
3
+ # This source code is licensed under the license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from PIL import Image, ImageFilter
7
+ from loguru import logger
8
+ import requests
9
+ import base64
10
+ import json
11
+ import io
12
+
13
+ # 接口 URL
14
+ t2i_url = 'https://magicarena.bytedance.com/api/evaluate/v1/algo/process'
15
+
16
+ #注意:正式上线环境需要不设置header
17
+ headers = {
18
+ 'X-TT-ENV': 'ppe_general_20',
19
+ 'X-USE-PPE': '1'
20
+ }
21
+
22
+ class SeedT2ICaller():
23
+ def __init__(self, cfg, *args, **kwargs):
24
+ self.cfg = cfg
25
+
26
+ def generate(self, text, *args, **kwargs):
27
+ try:
28
+ logger.info("Generate images ...")
29
+ req_json = json.dumps({
30
+ "prompt": str(text),
31
+ "use_sr": True,
32
+ "model_version": "general_v2.0_L",
33
+ "req_schedule_conf": "general_v20_9B_pe"
34
+ # "width": 64,
35
+ # "height": 64
36
+ })
37
+ logger.info(f"{req_json}")
38
+ # 请求发送
39
+ response = requests.post(
40
+ t2i_url,
41
+ headers=headers,
42
+ data={
43
+ 'AlgoType': 1,
44
+ 'ReqJson': req_json,
45
+ }
46
+ )
47
+ logger.info(f"header: {response.headers}")
48
+ if response.status_code != 200:
49
+ return None, False
50
+ resp = response.json()
51
+ if resp.get('code',{}) != 0:
52
+ logger.info(f"response error {resp}")
53
+ return None, False
54
+
55
+ binary_data1 = resp.get('data', {}).get('BinaryData')
56
+ binary_data = binary_data1[0]
57
+ #logger.info(f"binary_data: {binary_data}")
58
+ image = Image.open(io.BytesIO(base64.b64decode(binary_data)))
59
+ #image.save('./t2i_image.png')
60
+ image = image.resize((self.cfg['resolution'], self.cfg['resolution']))
61
+ return image, True
62
+
63
+ except Exception as e:
64
+ logger.exception("An error occurred during image generation.")
65
+ return None, False
66
+
67
+ class SeedEditCaller():
68
+ def __init__(self, cfg, *args, **kwargs):
69
+ self.cfg = cfg
70
+
71
+ def edit(self, image, edit, cfg_scale=0.5, *args, **kwargs):
72
+ try:
73
+ image_bytes = io.BytesIO()
74
+ image.save(image_bytes, format='JPEG') # 或 format='PNG'
75
+ logger.info("Edit images ...")
76
+ req_json = json.dumps({
77
+ "prompt": str(edit),
78
+ "model_version": "byteedit_v2.0",
79
+ "scale": cfg_scale,
80
+ })
81
+ logger.info(f"{req_json}")
82
+ binary =base64.b64encode(image_bytes.getvalue()).decode('utf-8')
83
+ # 请求发送
84
+ response = requests.post(
85
+ t2i_url,
86
+ headers=headers,
87
+ data=json.dumps({
88
+ 'AlgoType': 2,
89
+ 'ReqJson': req_json,
90
+ 'BinaryData': [binary]
91
+ # 'Base': base
92
+ })
93
+ )
94
+
95
+ logger.info(f"header: {response.headers}")
96
+ if response.status_code != 200:
97
+ return None, False
98
+ resp = response.json()
99
+ if resp.get('code',{}) != 0:
100
+ logger.info(f"response error {resp}")
101
+ return None, False
102
+
103
+ binary_data = resp.get('data', {}).get('BinaryData')
104
+ image = Image.open(io.BytesIO(base64.b64decode(binary_data[0])))
105
+ return image, True
106
+
107
+ except Exception as e:
108
+ logger.exception("An error occurred during image generation.")
109
+ return None, False
110
+
111
+
112
+
113
+ if __name__ == "__main__":
114
+ cfg_t2i = {
115
+ "resolution": 611
116
+ }
117
+ model_t2i = SeedT2ICaller(cfg_t2i)
118
+ model_t2i.generate("a beautiful girl")
119
+
120
+ image_path = "./t2i_image.png"
121
+ with open(image_path, 'rb') as image:
122
+ image_bytes = image.read()
123
+ model_edit = SeedEditCaller(cfg_t2i)
124
+ model_edit.edit(image=image_bytes,edit="please edit to a good man")
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio
2
+ numpy
3
+ loguru
4
+ Pillow
5
+ requests
uni_test/test.jpg ADDED