jiangchen16 commited on
Commit
3c3804b
·
1 Parent(s): 0cb5e75

initial commit

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ font/*.ttf filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,12 @@
1
  ---
2
  title: JoyType
3
- emoji: 📊
4
- colorFrom: gray
5
  colorTo: blue
6
  sdk: gradio
7
- sdk_version: 4.37.2
8
  app_file: app.py
9
  pinned: false
10
- license: apache-2.0
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: JoyType
3
+ emoji: 🔥
4
+ colorFrom: green
5
  colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 3.50.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # encoding=utf8
2
+
3
+
4
+ import os
5
+ import cv2
6
+ import gradio as gr
7
+ import numpy as np
8
+ import re
9
+ import json
10
+
11
+ from huggingface_hub import login
12
+ from functions import *
13
+ from gradio.components import Component
14
+
15
+ login(token=os.getenv('LOGIN_TOKEN'))
16
+ css = './css/style.css'
17
+
18
+ # Initial a Gradio Block with specific theme
19
+ block = gr.Blocks(
20
+ theme=gr.themes.Base(),
21
+ css=css
22
+ ).queue()
23
+
24
+ # Load javascript plugin
25
+ with open('javascript/bboxHint.js', 'r', encoding="utf-8") as file:
26
+ value = file.read()
27
+ escaped_value = json.dumps(value)
28
+
29
+ with block:
30
+ block.load(
31
+ fn=None,
32
+ _js=f"""() => {{
33
+ const script = document.createElement("script");
34
+ const text = document.createTextNode({escaped_value});
35
+ script.appendChild(text);
36
+ document.head.appendChild(script);
37
+ }}"""
38
+ )
39
+ gr.HTML(
40
+ '<div style="text-align: center; margin: 20px auto;"> \
41
+ <h1 style="font-size:5em">JoyType</h1> \
42
+ <h1 style="font-size:2.5em">A Robust Design for Multilingual Visual Text Creation</h1> \
43
+ </div>'
44
+ )
45
+ with gr.Row():
46
+ with gr.Column(scale=3):
47
+ with gr.Accordion('Basic Settings(基础设置)', open=True):
48
+ with gr.Row(variant='compact'):
49
+ usr_prompt = gr.Textbox(label='Prompt(提示词)', elem_id='usr_prompt')
50
+ with gr.Row(variant='compact'):
51
+ base_model = gr.Dropdown(
52
+ value='JoyType.v1.0', choices=model_list,
53
+ label='Base Model(基模型)', elem_id='base_model', allow_custom_value=False
54
+ )
55
+
56
+ with gr.Accordion('Advanced Settings(高级设置)', open=False):
57
+ with gr.Row(variant='compact'):
58
+ image_width = gr.Slider(label='Image Width(宽度)', minimum=256, maximum=768, value=512, step=32)
59
+ image_height = gr.Slider(label='Image Height(高度)', minimum=256, maximum=768, value=512, step=32)
60
+ with gr.Row(variant='compact'):
61
+ num_samples = gr.Slider(label='Samples(生成数量)', minimum=1, maximum=4, value=2, step=1)
62
+ inference_steps = gr.Slider(label='Steps(推理步数)', minimum=10, maximum=50, value=20, step=1)
63
+ with gr.Row(variant='compact'):
64
+ conditioning_scale = gr.Slider(label='Text Strength(文字强度)', minimum=0.1, maximum=2., value=1., step=0.1)
65
+ cfg_scale = gr.Slider(label='CFG Scale(CFG制强度)', minimum=1, maximum=20, value=7.5, step=0.5)
66
+ with gr.Row(variant='compact'):
67
+ seed = gr.Slider(label='Seed(随机种子)', minimum=-1, maximum=2147483647, value=-1, step=1)
68
+ scheduler_name = gr.Dropdown(
69
+ value='PNDM', choices=[
70
+ 'PNDM', 'LMS', 'Euler', 'DPM', 'DDIM', 'Heun', 'Euler-Ancestral'
71
+ ],
72
+ label='Scheduler(采样器)', allow_custom_value=False
73
+ )
74
+ with gr.Row(variant='compact'):
75
+ a_prompt = gr.Textbox(
76
+ label='Added Prompt(附加提示词)', max_lines=2,
77
+ value='best quality, extremely detailed, supper legible text, '
78
+ 'clear text edges, clear strokes, neat writing, no watermarks'
79
+ )
80
+ with gr.Row(variant='compact'):
81
+ n_prompt = gr.Textbox(
82
+ label='Negative Prompt(负向提示词)', max_lines=2,
83
+ value='low-res, bad anatomy, extra digit, fewer digits, cropped, worst quality, '
84
+ 'low quality, watermark, unreadable text, messy words, distorted text, '
85
+ 'disorganized writing, advertising picture'
86
+ )
87
+
88
+ base_model.change(
89
+ fn=change_settings,
90
+ inputs=base_model,
91
+ outputs=[inference_steps, cfg_scale, scheduler_name]
92
+ )
93
+
94
+ with gr.Row():
95
+ with gr.Tab('Text Editing(文字编辑)', elem_id='MD-tab-t2i'):
96
+ with gr.Row(variant='compact'):
97
+ choice = gr.Slider(
98
+ label=f'Text Boxes(可编辑文字框)',
99
+ minimum=0, maximum=8, step=1, value=BBOX_INI_NUM
100
+ )
101
+
102
+ with gr.Row():
103
+ with gr.Column(scale=2):
104
+ rect_img = gr.Image(
105
+ value=create_canvas(), label='Rect Position',
106
+ elem_id='MD-bbox-rect-t2i', show_label=False, visible=True,
107
+ height=300
108
+ )
109
+ with gr.Column(scale=3):
110
+ rect_cb_list: list[Component] = []
111
+ rect_box_list: list[Component] = []
112
+ rect_font_name_list: list[Component] = []
113
+ rect_usr_text_list: list[Component] = []
114
+
115
+ with gr.Column():
116
+ with gr.Row(elem_id='row_show'):
117
+ with gr.Column(scale=1, min_width=20):
118
+ gr.Markdown('<p align="center">Font(字体)</p>', elem_id='markdown_1')
119
+ with gr.Column(scale=2, min_width=20):
120
+ gr.Markdown('<p align="center">Text(文字内容)</p>', elem_id='markdown_2')
121
+
122
+ row_layout = [gr.Row() for _ in range(BBOX_MAX_NUM)]
123
+ for i in range(BBOX_MAX_NUM):
124
+ visible = True if i < BBOX_INI_NUM else False
125
+ with row_layout[i]:
126
+ fn = gr.Dropdown(
127
+ choices=font_list,
128
+ label='Font(字体)', value='CHN-华文行楷', visible=visible,
129
+ show_label=False, scale=1, allow_custom_value=False,
130
+ min_width=90, elem_id=f'font_input_{i}', container=False
131
+ )
132
+ ut = gr.Textbox(
133
+ label='Text(文字内容)', visible=visible, scale=2,
134
+ show_label=False, elem_id=f'text_input_{i}', container=False, max_lines=1
135
+ )
136
+ e = gr.Checkbox(label=f'{i}', value=visible, visible=False, min_width=10)
137
+
138
+ x = gr.Slider(label='x', value=0.4, minimum=0.0, maximum=1.0, step=0.0001,
139
+ elem_id=f'MD-t2i-{i}-x',
140
+ visible=False)
141
+ y = gr.Slider(label='y', value=0.4, minimum=0.0, maximum=1.0, step=0.0001,
142
+ elem_id=f'MD-t2i-{i}-y',
143
+ visible=False)
144
+ w = gr.Slider(label='w', value=0.2, minimum=0.0, maximum=1.0, step=0.0001,
145
+ elem_id=f'MD-t2i-{i}-w',
146
+ visible=False)
147
+ h = gr.Slider(label='h', value=0.2, minimum=0.0, maximum=1.0, step=0.0001,
148
+ elem_id=f'MD-t2i-{i}-h',
149
+ visible=False)
150
+ x.change(fn=None, inputs=x, outputs=x, _js=f'v => onBoxChange({i}, "x", v)',
151
+ show_progress=False, queue=False)
152
+ y.change(fn=None, inputs=y, outputs=y, _js=f'v => onBoxChange({i}, "y", v)',
153
+ show_progress=False, queue=False)
154
+ w.change(fn=None, inputs=w, outputs=w, _js=f'v => onBoxChange({i}, "w", v)',
155
+ show_progress=False, queue=False)
156
+ h.change(fn=None, inputs=h, outputs=h, _js=f'v => onBoxChange({i}, "h", v)',
157
+ show_progress=False, queue=False)
158
+ e.change(fn=None, inputs=e, outputs=e, _js=f'e => onBoxEnableClick({i}, e)',
159
+ queue=False)
160
+
161
+ rect_cb_list.extend([e])
162
+ rect_box_list.extend([x, y, w, h])
163
+ rect_font_name_list.extend([fn])
164
+ rect_usr_text_list.extend([ut])
165
+
166
+ choice.change(
167
+ fn=update_box_num,
168
+ inputs=[choice],
169
+ outputs=[
170
+ *rect_cb_list, *rect_font_name_list, *rect_usr_text_list, *rect_box_list
171
+ ]
172
+ )
173
+ with gr.Row():
174
+ gr.Markdown('')
175
+ run_edit = gr.Button(value='Run(运行)', elem_classes='run', elem_id='run_edit')
176
+ gr.Markdown('')
177
+ with gr.Row():
178
+ with gr.Accordion(label='Examples(示例)', open=True):
179
+ img_container = gr.Image(visible=False, label='Text Layout(文字布局)')
180
+ example_id = gr.Textbox(value=-1, visible=False, label='ID(编号)')
181
+ gen_examples = gr.Examples(
182
+ [
183
+ [1, 'templates/1.png', 'landscape, Chinese style, ink peaks, poster', model_list[0], 1648703813, 3, 1],
184
+ [2, 'templates/2.png', 'a clock and medicine bottle has texts and "time"', model_list[0], 1654615998, 2, 1],
185
+ [3, 'templates/3.png', '漂亮的风景照,很多山峰,清澈的湖水', model_list[3], 2078698098, 3, 1],
186
+ [4, 'templates/4.png', 'a vodka, on the bar, dim background', model_list[2], 443791646, 3, 1],
187
+ [5, 'templates/5.png', '画有玫瑰的卡片,明亮的背景', model_list[4], 516210890, 2, 1],
188
+ [6, 'templates/6.png', 'posters on the table, with pens, clear background, starry sky, moon', model_list[1], 228167646, 4, 1],
189
+ [7, 'templates/7.png', 'snowy landscape, domed cabin, winter scene, cozy atmosphere, soft lighting', model_list[5], 695897181, 3, 1],
190
+ [8, 'templates/8.png', '一张关于健康教育的卡片,上面有一些文字,有一些食物图标,背景里有一些水果喝饮料的图标,且背景是模糊的', model_list[1], 936188591, 6, 1],
191
+ ],
192
+ [example_id, img_container, usr_prompt, base_model, seed, choice, num_samples],
193
+ examples_per_page=5,
194
+ label=''
195
+ )
196
+
197
+ example_id.change(
198
+ fn=load_box_list,
199
+ inputs=[example_id, choice],
200
+ outputs=[
201
+ *rect_cb_list, *rect_font_name_list, *rect_usr_text_list, *rect_box_list, example_id
202
+ ]
203
+ )
204
+
205
+ rect_img.clear(re_edit, None, [*rect_box_list, rect_img, image_width, image_height])
206
+ image_width.release(resize_w, [image_width, rect_img], rect_img)
207
+ image_height.release(resize_h, [image_height, rect_img], rect_img)
208
+
209
+ with gr.Column(scale=2):
210
+ with gr.Row():
211
+ result_gallery = gr.Gallery(
212
+ label='Result(结果)', show_label=True, preview=True, columns=8,
213
+ allow_preview=True, elem_id='gallery'
214
+ )
215
+ with gr.Row():
216
+ with gr.Tab("Introduction"):
217
+ gr.Markdown('<span style="color:#3B5998;font-size:20px">What we can do</span>')
218
+ gr.Markdown(
219
+ '<span style="color:black;font-size:15px">Generating images with accurately represented text in multi-language.</span>')
220
+ gr.Markdown('<span style="color:#3B5998;font-size:20px">How to use</span>')
221
+ gr.Markdown(
222
+ '<span style="color:black;font-size:15px">Enter a description of the image you want to generate in the "Prompt" text box.</span>')
223
+ gr.Markdown('<span style="color:#3B5998;font-size:18px">Text Editing</span>')
224
+ gr.Markdown(
225
+ '<span style="color:black;font-size:15px">You can drag the "Text Boxes" slider to set the number of text to be laid out, '
226
+ 'and set the corresponding font and text content respectively, Note that there must be no overlap between the text boxes, '
227
+ 'or the model will not generate an image.</span>')
228
+ gr.Markdown(
229
+ '<span style="color:black;font-size:15px">Finally, click the Run button to generate a picture!</span>')
230
+ with gr.Tab("说明"):
231
+ gr.Markdown('<span style="color:#3B5998;font-size:20px">我们能做什么</span>')
232
+ gr.Markdown('<span style="color:black;font-size:15px">在多种语言上生成具有准确文本的图像</span>')
233
+ gr.Markdown('<span style="color:#3B5998;font-size:20px">如何使用</span>')
234
+ gr.Markdown(
235
+ '<span style="color:black;font-size:15px">在“提示词”文本框中输入你想要生成的图片所对应的文字描述。</span>')
236
+ gr.Markdown('<span style="color:#3B5998;font-size:18px">文本编辑</span>')
237
+ gr.Markdown(
238
+ '<span style="color:black;font-size:15px">你可以拖动“可编辑文字框”滑块来设置需要布局的文字数量,并分别设置对应的字体和文字内容;'
239
+ '请注意,文本框之间不能有重叠,否则模型将不会生成图片。</span>')
240
+ gr.Markdown('<span style="color:black;font-size:15px">最后点击运行按钮,即可生成图片!</span>')
241
+ with gr.Row():
242
+ result_info = gr.Markdown('debug', visible=False)
243
+
244
+ args = [
245
+ num_samples, a_prompt, n_prompt,
246
+ conditioning_scale, cfg_scale, inference_steps, seed, usr_prompt,
247
+ rect_img, base_model, scheduler_name, gr.State(BBOX_MAX_NUM),
248
+ *(rect_cb_list + rect_box_list + rect_font_name_list + rect_usr_text_list)
249
+ ]
250
+ run_edit.click(
251
+ fn=process,
252
+ inputs=args,
253
+ outputs=[result_gallery, result_info]
254
+ )
255
+
256
+
257
+ if __name__ == "__main__":
258
+ block.launch(
259
+ server_name='0.0.0.0',
260
+ share=True,
261
+ )
css/style.css ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body, html {
2
+ height: 100%;
3
+ margin: 0;
4
+ }
5
+ .gradio_container {
6
+ height: 100%;
7
+ }
8
+
9
+ /**gallery**/
10
+ #gallery {
11
+ height: 400px;
12
+ width: 100%;
13
+ }
14
+
15
+ /**every font dropdown in editing**/
16
+ #font_input_0 input {
17
+ color: #ff0000;
18
+ font-size: 15px;
19
+ }
20
+ #font_input_1 input {
21
+ color: #ff9900;
22
+ font-size: 15px;
23
+ }
24
+ #font_input_2 input {
25
+ color: #996633;
26
+ font-size: 15px;
27
+ }
28
+ #font_input_3 input {
29
+ color: #33cc33;
30
+ font-size: 15px;
31
+ }
32
+ #font_input_4 input {
33
+ color: #33cccc;
34
+ font-size: 15px;
35
+ }
36
+ #font_input_5 input {
37
+ color: #0066ff;
38
+ font-size: 15px;
39
+ }
40
+ #font_input_6 input {
41
+ color: #ff3399;
42
+ font-size: 15px;
43
+ }
44
+ #font_input_7 input {
45
+ color: #cc00cc;
46
+ font-size: 15px;
47
+ }
48
+ #font_input_0.block.svelte-90oupt {
49
+ height: 30px;
50
+ }
51
+ #font_input_1.block.svelte-90oupt {
52
+ height: 30px;
53
+ }
54
+ #font_input_2.block.svelte-90oupt {
55
+ height: 30px;
56
+ }
57
+ #font_input_3.block.svelte-90oupt {
58
+ height: 30px;
59
+ }
60
+ #font_input_4.block.svelte-90oupt {
61
+ height: 30px;
62
+ }
63
+ #font_input_5.block.svelte-90oupt {
64
+ height: 30px;
65
+ }
66
+ #font_input_6.block.svelte-90oupt {
67
+ height: 30px;
68
+ }
69
+ #font_input_7.block.svelte-90oupt {
70
+ height: 30px;
71
+ }
72
+
73
+ /**every text box in editing**/
74
+ #text_input_0 input {
75
+ font-size: 15px;
76
+ }
77
+ #text_input_1 input {
78
+ font-size: 15px;
79
+ }
80
+ #text_input_2 input {
81
+ font-size: 15px;
82
+ }
83
+ #text_input_3 input {
84
+ font-size: 15px;
85
+ }
86
+ #text_input_4 input {
87
+ font-size: 15px;
88
+ }
89
+ #text_input_5 input {
90
+ font-size: 15px;
91
+ }
92
+ #text_input_6 input {
93
+ font-size: 15px;
94
+ }
95
+ #text_input_7 input {
96
+ font-size: 15px;
97
+ }
98
+ #text_input_0.block.svelte-90oupt {
99
+ height: 30px;
100
+ }
101
+ #text_input_1.block.svelte-90oupt {
102
+ height: 30px;
103
+ }
104
+ #text_input_2.block.svelte-90oupt {
105
+ height: 30px;
106
+ }
107
+ #text_input_3.block.svelte-90oupt {
108
+ height: 30px;
109
+ }
110
+ #text_input_4.block.svelte-90oupt {
111
+ height: 30px;
112
+ }
113
+ #text_input_5.block.svelte-90oupt {
114
+ height: 30px;
115
+ }
116
+ #text_input_6.block.svelte-90oupt {
117
+ height: 30px;
118
+ }
119
+ #text_input_7.block.svelte-90oupt {
120
+ height: 30px;
121
+ }
122
+
123
+ #row_show {
124
+ display: block
125
+ height: 25px
126
+ }
127
+ #markdown_1 {
128
+ display: block;
129
+ height: 25px;
130
+ }
131
+ #markdown_2 {
132
+ display: block;
133
+ height: 25px;
134
+ }
135
+ #markdown_1 span {
136
+ display: block;
137
+ height: 25px;
138
+ }
139
+ #markdown_2 span {
140
+ display: block;
141
+ height: 25px;
142
+ }
143
+
144
+ #run_edit {
145
+ background-color: #ff4500;
146
+ color: white;
147
+ }
148
+ #run_upload {
149
+ background-color: #ff4500;
150
+ color: white;
151
+ }
152
+
153
+ /**************************************
154
+ footer.svelte-1ax1toq {
155
+ display: none !important;
156
+ }
157
+ /**************************************/
font/Alice.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3b7ea628cb473d4b4737c4ffcd3f760bf6ca325d1344a134579ca636e33d9917
3
+ size 128720
font/Aoyagireisyosimo.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a4c55ad5f72e65a482931d967725e97ff206eb3019c87281d9e5514a63bb8db9
3
+ size 4412684
font/Automatons.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:951850cc3b0839d886fcb773accca92c22f6eda0178b76a72c81377978100408
3
+ size 6784
font/BebasNeue.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:830ea186acffc2316ed1a4e42319246ba3b46b04e33a211079249bf901193f04
3
+ size 57676
font/Caveat.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:263493f012c8ffbf3a69a38d43ce494c42d1cb2d44b7cb9eff10095f08fce719
3
+ size 391068
font/ChosunGs.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4e191bc30d23ce34797dcaf7a0965dedd67a2d85cc5dd87325ee96626cba7bea
3
+ size 9260104
font/Dongle.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:010703af6b86a860847eea86823387e05669faa75e81e6a0040398906a0f1fe7
3
+ size 4458436
font/Filthyrich.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6eb72bd16d5613612734a103a9cd9a7ffba83857675c8b53891eb2c3b8a3e582
3
+ size 125132
font/GlTsukiji.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6907481e7548bc723ef312e7508b21ea2e4b30313a13e7e2042ef4ad0953f7c3
3
+ size 334440
font/Gloock.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:39e2af503dfb3d1e093f0178863e2b163c59cdc4f4c5d152d50f51da19baf8fa
3
+ size 94940
font/GodoMaum.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3ea6333b8a9b875d778598debd260695770a813fdbdd0d233845329151e43ffd
3
+ size 2760760
font/KouzanMouhitu.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:582f269574e71336f1ee8318664ff6cfafd84293b3267ddf39410015713c951e
3
+ size 8238216
font/Lemon.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:664fb2be44c5a08ba5ec89d06c3d523e0c2650a18922e667b8d9573a88ec37c4
3
+ size 73592
font/MKyrill.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:da5256add8bb73db799e2f354ea9e4bf2fd893c2d3af583021b09a67f892962f
3
+ size 81868
font/Nextstep.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:eae79e803499e66a6f15a8fab6e19bccbeeb2fe3b51722894ff2f1584fb9cafa
3
+ size 19952
font/Okesip.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ea383c52f8fe1d4473d1a0d8d5cca42fda375d9f317bf42a0bd21251a8976e28
3
+ size 74056
font/Otomanopee.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cb4bbcf825544a5102991f5118bbaa2440977ea1f4d5446c22963d70fd435602
3
+ size 366684
font/Playwrite.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:17810b62177426ffbe7b661598f8d1a67254daad70aac6a375c6ee1863a3c711
3
+ size 344044
font/Shrikhand.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:00269b4efb014de272c9313e16b7e166617c6fdac819a2bb063ef1e371b5801b
3
+ size 213868
font/UnDotum.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5b8373e126bb61f59105cf7f54a47eb1b089c2b0aacb70c6cd688bd8ea76cdc9
3
+ size 3656228
font/华文新魏.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:361dc6d522d417fc5705948e65d191f7826147d390980f4cbdcfbca4a0200290
3
+ size 4044420
font/华文行楷.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6e893a5a618b39f317362efd77f3c6aeb16149328cb66872c9db8cb457a71d32
3
+ size 4009504
font/宅家麦克笔.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:15cec9ab9565e0851e144144f7023c4c59f4fcdea96710a75cf668049f79b3df
3
+ size 17093408
font/巴蜀墨迹.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d81299f3ed474a0a9ed80db21410c604c1ca561f9d36b8ff62ce4137d91c0cc8
3
+ size 7171284
font/斑马字类.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:93a253e524b7951e2394e1ac2bf981aa268ce0ae4b11934682de6c42f19354e1
3
+ size 2719964
font/清松手写体.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ddd51d365ff4b1b5c58e2cc722226cd3109e9b33fcd6538b6599cf308dbc90e3
3
+ size 6348828
font/演示夏行楷.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fac1f0ec044b63aa45c0775c17a2f03cbf6427b4bd7b93da87dde9e7e2020cfc
3
+ size 10073644
font/辰宇落雁体.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1f04002cc0906d2bda376fb133d4a8160805db0113e92baf6da54aeafde88bd7
3
+ size 4511804
font/雷盖体.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:33fd784e6aa6b2f8e8e6064280ea6fc473246b6371884155d75c55577e6db4c2
3
+ size 4336920
font/青柳隶书.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a4c55ad5f72e65a482931d967725e97ff206eb3019c87281d9e5514a63bb8db9
3
+ size 4412684
font/鸿雷板书简体.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:30a8a257480dbe55fd872722fb6ec52a7939ea0d34ddfcedbdf3595430d62d6b
3
+ size 11651532
functions.py ADDED
@@ -0,0 +1,522 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import cv2
4
+ import random
5
+ import numpy as np
6
+ import gradio as gr
7
+ import torch
8
+
9
+ from zhipuai import ZhipuAI
10
+ from pytorch_lightning import seed_everything
11
+ from pprint import pprint
12
+ from PIL import Image, ImageDraw, ImageFont
13
+ from diffusers import (
14
+ ControlNetModel,
15
+ StableDiffusionControlNetPipeline,
16
+ )
17
+ from diffusers import (
18
+ DDIMScheduler,
19
+ PNDMScheduler,
20
+ EulerAncestralDiscreteScheduler,
21
+ DPMSolverMultistepScheduler,
22
+ EulerDiscreteScheduler,
23
+ LMSDiscreteScheduler,
24
+ HeunDiscreteScheduler
25
+ )
26
+ from controlnet_aux import (
27
+ PidiNetDetector,
28
+ HEDdetector
29
+ )
30
+
31
+
32
+ BBOX_MAX_NUM = 8
33
+ BBOX_INI_NUM = 0
34
+ MAX_LENGTH = 20
35
+ device = 'cuda'
36
+ pipeline = None
37
+ pre_pipeline = None
38
+ model_root = os.getenv('REPO_ROOT')
39
+ scheduler_root = f'{model_root}/Scheduler'
40
+ model_list =[
41
+ 'JoyType.v1.0', 'RevAnimated-animation-动漫', 'GhostMix-animation-动漫',
42
+ 'rpg.v5-fantasy_realism-奇幻写实', 'midjourneyPapercut-origami-折纸版画',
43
+ 'dvarchExterior-architecture-建筑', 'awpainting.v13-portrait-人物肖像'
44
+ ]
45
+ chn_example_dict = {
46
+ '漂亮的风景照,很多山峰,清澈的湖水': 'beautiful landscape, many peaks, clear lake',
47
+ '画有玫瑰的卡片,明亮的背景': 'a card with roses, bright background',
48
+ '一张关于健康教育的卡片,上面有一些文字,有一些食物图标,背景里有一些水果喝饮料的图标,且背景是模糊的': \
49
+ 'a card for health education, with some writings on it, '
50
+ 'food icons on the card, some fruits and drinking in the background, blur background '
51
+ }
52
+ match_dict = {
53
+ 'JoyType.v1.0': 'JoyType-v1-1M',
54
+ 'RevAnimated-animation-动漫': 'rev-animated-v1-2-2',
55
+ 'GhostMix-animation-动漫': 'GhostMix_V2.0',
56
+ 'rpg.v5-fantasy_realism-奇幻写实': 'rpg_v5',
57
+ 'midjourneyPapercut-origami-折纸版画': 'midjourneyPapercut_v1',
58
+ 'dvarchExterior-architecture-建筑': 'dvarchExterior',
59
+ 'awpainting.v13-portrait-人物肖像': 'awpainting_v13'
60
+ }
61
+ font_list = [
62
+ 'CHN-华文行楷',
63
+ 'CHN-华文新魏',
64
+ 'CHN-清松手写体',
65
+ 'CHN-巴蜀墨迹',
66
+ 'CHN-雷盖体',
67
+ 'CHN-演示夏行楷',
68
+ 'CHN-鸿雷板书简体',
69
+ 'CHN-斑马字类',
70
+ 'CHN-青柳隶书',
71
+ 'CHN-辰宇落雁体',
72
+ 'CHN-宅家麦克笔',
73
+ 'ENG-Playwrite',
74
+ 'ENG-Okesip',
75
+ 'ENG-Shrikhand',
76
+ 'ENG-Nextstep',
77
+ 'ENG-Filthyrich',
78
+ 'ENG-BebasNeue',
79
+ 'ENG-Gloock',
80
+ 'ENG-Lemon',
81
+ 'RUS-Automatons',
82
+ 'RUS-MKyrill',
83
+ 'RUS-Alice',
84
+ 'RUS-Caveat',
85
+ 'KOR-ChosunGs',
86
+ 'KOR-Dongle',
87
+ 'KOR-GodoMaum',
88
+ 'KOR-UnDotum',
89
+ 'JPN-GlTsukiji',
90
+ 'JPN-Aoyagireisyosimo',
91
+ 'JPN-KouzanMouhitu',
92
+ 'JPN-Otomanopee'
93
+ ]
94
+
95
+
96
+ def change_settings(base_model):
97
+ if base_model == model_list[0]:
98
+ return gr.update(value=20), gr.update(value=7.5), gr.update(value='PNDM')
99
+ elif base_model == model_list[1]:
100
+ return gr.update(value=30), gr.update(value=8.5), gr.update(value='Euler')
101
+ elif base_model == model_list[2]:
102
+ return gr.update(value=32), gr.update(value=8.5), gr.update(value='Euler')
103
+ elif base_model == model_list[3]:
104
+ return gr.update(value=20), gr.update(value=7.5), gr.update(value='DPM')
105
+ elif base_model == model_list[4]:
106
+ return gr.update(value=25), gr.update(value=6.5), gr.update(value='Euler')
107
+ elif base_model == model_list[5]:
108
+ return gr.update(value=25), gr.update(value=8.5), gr.update(value='Euler')
109
+ elif base_model == model_list[6]:
110
+ return gr.update(value=25), gr.update(value=7), gr.update(value='DPM')
111
+ else:
112
+ pass
113
+
114
+
115
+ def update_box_num(choice):
116
+ update_list_1 = [] # checkbox
117
+ update_list_2 = [] # font
118
+ update_list_3 = [] # text
119
+ update_list_4 = [] # bounding box
120
+ for i in range(BBOX_MAX_NUM):
121
+ if i < choice:
122
+ update_list_1.append(gr.update(value=True))
123
+ update_list_2.append(gr.update(visible=True))
124
+ update_list_3.append(gr.update(visible=True))
125
+ update_list_4.extend([gr.update(visible=False) for _ in range(4)])
126
+ else:
127
+ update_list_1.append(gr.update(value=False))
128
+ update_list_2.append(gr.update(visible=False, value='CHN-华文行楷'))
129
+ update_list_3.append(gr.update(visible=False, value=''))
130
+ update_list_4.extend([
131
+ gr.update(visible=False, value=0.4),
132
+ gr.update(visible=False, value=0.4),
133
+ gr.update(visible=False, value=0.2),
134
+ gr.update(visible=False, value=0.2)
135
+ ])
136
+
137
+ return *update_list_1, *update_list_2, *update_list_3, *update_list_4
138
+
139
+
140
+ def load_box_list(example_id, choice):
141
+ with open(f'templates/{example_id}.json', 'r') as f:
142
+ info = json.load(f)
143
+ update_list1 = []
144
+ update_list2 = []
145
+ update_list3 = []
146
+ update_list4 = []
147
+
148
+ for i in range(BBOX_MAX_NUM):
149
+ visible = info['visible'][i]
150
+ pos = info['pos'][i * 4: (i + 1) * 4]
151
+ update_list1.append(gr.update(value=visible))
152
+ update_list2.append(gr.update(value=info['font'][i], visible=visible))
153
+ update_list3.append(gr.update(value=info['text'][i], visible=visible))
154
+ update_list4.extend([
155
+ gr.update(value=pos[0]),
156
+ gr.update(value=pos[1]),
157
+ gr.update(value=pos[2]),
158
+ gr.update(value=pos[3])
159
+ ])
160
+
161
+ return *update_list1, *update_list2, \
162
+ *update_list3, *update_list4, gr.update(value=-1)
163
+
164
+
165
+ def re_edit():
166
+ global BBOX_MAX_NUM
167
+ update_list = []
168
+ for i in range(BBOX_MAX_NUM):
169
+ update_list.extend([gr.update(value=0.4), gr.update(value=0.4), gr.update(value=0.2),
170
+ gr.update(value=0.2)])
171
+ return *update_list, \
172
+ gr.Image(
173
+ value=create_canvas(),
174
+ label='Rect Position', elem_id='MD-bbox-rect-t2i',
175
+ show_label=False, visible=True
176
+ ), \
177
+ gr.Slider(value=512), gr.Slider(value=512)
178
+
179
+
180
+ def resize_w(w, img):
181
+ return cv2.resize(img, (w, img.shape[0]))
182
+
183
+
184
+ def resize_h(h, img):
185
+ return cv2.resize(img, (img.shape[1], h))
186
+
187
+
188
+ def create_canvas(w=512, h=512, c=3, line=5):
189
+ image = np.full((h, w, c), 200, dtype=np.uint8)
190
+ for i in range(h):
191
+ if i % (w // line) == 0:
192
+ image[i, :, :] = 150
193
+ for j in range(w):
194
+ if j % (w // line) == 0:
195
+ image[:, j, :] = 150
196
+ image[h // 2 - 8:h // 2 + 8, w // 2 - 8:w // 2 + 8, :] = [200, 0, 0]
197
+ return image
198
+
199
+
200
+ def canny(img):
201
+ low_threshold = 64
202
+ high_threshold = 100
203
+
204
+ img = cv2.Canny(img, low_threshold, high_threshold)
205
+ img = img[:, :, None]
206
+ img = np.concatenate([img, img, img], axis=2)
207
+ return Image.fromarray(img)
208
+
209
+
210
+ def judge_overlap(coord_list1, coord_list2):
211
+ judge = coord_list1[0] < coord_list2[2] and coord_list1[2] > coord_list2[0] \
212
+ and coord_list1[1] < coord_list2[3] and coord_list1[3] > coord_list2[1]
213
+ return judge
214
+
215
+
216
+ def parse_render_list(box_list, shape, box_num):
217
+ width = shape[0]
218
+ height = shape[1]
219
+ polygons = []
220
+ font_names = []
221
+ texts = []
222
+ valid_list = box_list[:box_num]
223
+ pos_list = box_list[box_num: 5 * box_num]
224
+ font_name_list = box_list[5 * box_num: 6 * box_num]
225
+ text_list = box_list[6 * box_num: 7 * box_num]
226
+ empty_flag = False
227
+
228
+ print(font_name_list, text_list)
229
+
230
+ for i, valid in enumerate(valid_list):
231
+ if valid:
232
+ pos = pos_list[i * 4: (i + 1) * 4]
233
+ top_left_x = int(pos[0] * width)
234
+ top_left_y = int(pos[1] * height)
235
+ w = int(pos[2] * width)
236
+ h = int(pos[3] * height)
237
+ font_name = str(font_name_list[i])
238
+ text = str(text_list[i])
239
+ if text == '':
240
+ empty_flag = True
241
+ text = 'JoyType'
242
+ if w <= 0 or h <= 0:
243
+ gr.Warning(f'Area of the box{i + 1} cannot be zero!')
244
+ return [], False
245
+ polygon = [
246
+ top_left_x,
247
+ top_left_y,
248
+ w, h
249
+ ]
250
+ try:
251
+ assert font_name in font_list
252
+ font_name = font_name.split('-')[-1]
253
+ except Exception as e:
254
+ gr.Warning('Please choose a correct font!')
255
+ return [], False
256
+
257
+ polygons.append(polygon)
258
+ font_names.append(font_name.split('-')[-1])
259
+ texts.append(text)
260
+
261
+ if empty_flag:
262
+ gr.Warning('Null strings will be filled automatically!')
263
+
264
+ for i in range(len(polygons)):
265
+ for j in range(i + 1, len(polygons)):
266
+ if judge_overlap(
267
+ [polygons[i][0], polygons[i][1], polygons[i][0] + polygons[i][2], polygons[i][1] + polygons[i][3]],
268
+ [polygons[j][0], polygons[j][1], polygons[j][0] + polygons[j][2], polygons[j][1] + polygons[j][3]]
269
+ ):
270
+ gr.Warning('Find overlapping boxes!')
271
+ return [], False
272
+
273
+ render_list = []
274
+ for i in range(len(polygons)):
275
+ text_dict = {}
276
+ text_dict['text'] = texts[i]
277
+ text_dict['polygon'] = polygons[i]
278
+ text_dict['font_name'] = font_names[i]
279
+ render_list.append(text_dict)
280
+
281
+ return render_list, True
282
+
283
+
284
+ def render_all_text(render_list, shape, threshold=512):
285
+ width = shape[0]
286
+ height = shape[1]
287
+ board = Image.new('RGB', (width, height), 'black')
288
+
289
+ for text_dict in render_list:
290
+ text = text_dict['text']
291
+ polygon = text_dict['polygon']
292
+ font_name = text_dict['font_name']
293
+ if len(text) > MAX_LENGTH:
294
+ text = text[:MAX_LENGTH]
295
+ gr.Warning(f'{text}... exceeds the maximum length {MAX_LENGTH} and has been cropped.')
296
+
297
+ w, h = polygon[2:]
298
+ vert = True if w < h else False
299
+ image4ratio = Image.new('RGB', (1024, 1024), 'black')
300
+ draw = ImageDraw.Draw(image4ratio)
301
+
302
+ try:
303
+ font = ImageFont.truetype(f'./font/{font_name}.ttf', encoding='utf-8', size=50)
304
+ except FileNotFoundError:
305
+ font = ImageFont.truetype(f'./font/{font_name}.otf', encoding='utf-8', size=50)
306
+
307
+ if not vert:
308
+ draw.text(xy=(0, 0), text=text, font=font, fill='white')
309
+ _, _, _tw, _th = draw.textbbox(xy=(0, 0), text=text, font=font)
310
+ _th += 1
311
+ else:
312
+ _tw, y_c = 0, 0
313
+ for c in text:
314
+ draw.text(xy=(0, y_c), text=c, font=font, fill='white')
315
+ _l, _t, _r, _b = font.getbbox(c)
316
+ _tw = max(_tw, _r - _l)
317
+ y_c += _b
318
+ _th = y_c + 1
319
+
320
+ ratio = (_th * w) / (_tw * h)
321
+ text_img = image4ratio.crop((0, 0, _tw, _th))
322
+ x_offset, y_offset = 0, 0
323
+ if 0.8 <= ratio <= 1.2:
324
+ text_img = text_img.resize((w, h))
325
+ elif ratio < 0.75:
326
+ resize_h = int(_th * (w / _tw))
327
+ text_img = text_img.resize((w, resize_h))
328
+ y_offset = (h - resize_h) // 2
329
+ else:
330
+ resize_w = int(_tw * (h / _th))
331
+ text_img = text_img.resize((resize_w, h))
332
+ x_offset = (w - resize_w) // 2
333
+
334
+ board.paste(text_img, (polygon[0] + x_offset, polygon[1] + y_offset))
335
+
336
+ return board
337
+
338
+
339
+ def load_pipeline(model_name, scheduler_name):
340
+ controlnet_path = os.path.join(model_root, f'{match_dict["JoyType.v1.0"]}')
341
+ model_path = os.path.join(model_root, model_name)
342
+ scheduler_name = scheduler_name.lower()
343
+
344
+ if scheduler_name == 'pndm':
345
+ scheduler = PNDMScheduler.from_pretrained(scheduler_root, subfolder='pndm')
346
+ if scheduler_name == 'lms':
347
+ scheduler = LMSDiscreteScheduler.from_pretrained(scheduler_root, subfolder='lms')
348
+ if scheduler_name == 'euler':
349
+ scheduler = EulerDiscreteScheduler.from_pretrained(scheduler_root, subfolder='euler')
350
+ if scheduler_name == 'dpm':
351
+ scheduler = DPMSolverMultistepScheduler.from_pretrained(scheduler_root, subfolder='dpm')
352
+ if scheduler_name == 'ddim':
353
+ scheduler = DDIMScheduler.from_pretrained(scheduler_root, subfolder='ddim')
354
+ if scheduler_name == 'heun':
355
+ scheduler = HeunDiscreteScheduler.from_pretrained(scheduler_root, subfolder='heun')
356
+ if scheduler_name == 'euler-ancestral':
357
+ scheduler = EulerAncestralDiscreteScheduler.from_pretrained(scheduler_root, subfolder='euler-ancestral')
358
+
359
+ controlnet = ControlNetModel.from_pretrained(
360
+ controlnet_path,
361
+ subfolder='controlnet',
362
+ torch_dtype=torch.float32
363
+ )
364
+ pipeline = StableDiffusionControlNetPipeline.from_pretrained(
365
+ model_path,
366
+ scheduler=scheduler,
367
+ controlnet=controlnet,
368
+ torch_dtype=torch.float32,
369
+ ).to(device)
370
+
371
+ return pipeline
372
+
373
+
374
+ def preprocess_prompt(prompt):
375
+ client = ZhipuAI(api_key=os.getenv('ZHIPU_API_KEY'))
376
+ response = client.chat.completions.create(
377
+ model="glm-4-0520",
378
+ messages=[
379
+ {
380
+ 'role': 'system',
381
+ 'content': '''
382
+ Stable Diffusion是一款利用深度学习的文生图模型,支持通过使用提示词来产生新的图像,描述要包含或省略的元素。
383
+ 我在这里引入Stable Diffusion算法中的Prompt概念,又被称为提示符。这里的Prompt通常可以用来描述图像,
384
+ 他由普通常见的单词构成,最好是可以在数据集来源站点找到的著名标签(比如Ddanbooru)。
385
+ 下面我将说明Prompt的生出步骤,这里的Prompt主要用于描述人物。在Prompt的生成中,你需要通过提示词来描述 人物属性,主题,外表,情绪,衣服,姿势,视角,动作,背景。
386
+ 用英语单词或短语甚至自然语言的标签来描述,并不局限于我给你的单词。然后将你想要的相似的提示词组合在一起,请使用英文半角,做分隔符,每个提示词不要带引号,并将这些按从最重要到最不重要的顺序 排列。
387
+ 另外请您注意,永远在每个 Prompt的前面加上引号里的内容,
388
+ “(((best quality))),(((ultra detailed))),(((masterpiece))),illustration,” 这是高质量的标志。
389
+ 人物属性中,1girl表示你生成了一个女孩,2girls表示生成了两个女孩,一次。另外再注意,Prompt中不能带有-和_。
390
+ 可以有空格和自然语言,但不要太多,单词不能重复。只返回Prompt。
391
+ '''
392
+ },
393
+ {
394
+ 'role': 'user',
395
+ 'content': prompt
396
+ }
397
+ ],
398
+ temperature=0.5,
399
+ max_tokens=2048,
400
+ top_p=1,
401
+ stream=False,
402
+ )
403
+
404
+ if response:
405
+ glm = []
406
+ glm_return_list = response.choices
407
+ for item in glm_return_list:
408
+ glm.append(item.message.content)
409
+
410
+ return {'flag': 1, 'data': glm}
411
+ else:
412
+ return {'flag': 0, 'data': {}}
413
+
414
+
415
+ def process(
416
+ num_samples,
417
+ a_prompt,
418
+ n_prompt,
419
+ conditioning_scale,
420
+ cfg_scale,
421
+ inference_steps,
422
+ seed,
423
+ usr_prompt,
424
+ rect_img,
425
+ base_model,
426
+ scheduler_name,
427
+ box_num,
428
+ *box_list
429
+ ):
430
+ if usr_prompt == '':
431
+ gr.Warning('Must input a prompt!')
432
+ return None, gr.Markdown('error')
433
+
434
+ if seed == -1:
435
+ seed = random.randint(0, 2147483647)
436
+ seed_everything(seed)
437
+
438
+ # Support Chinese Input
439
+ if usr_prompt in chn_example_dict.keys():
440
+ usr_prompt = chn_example_dict[usr_prompt]
441
+ else:
442
+ for ch in usr_prompt:
443
+ if '\u4e00' <= ch <= '\u9fff':
444
+ data = preprocess_prompt(usr_prompt)
445
+ if data['flag'] == 1:
446
+ usr_prompt = data['data'][0][1: -1]
447
+ else:
448
+ gr.Warning('Something went wrong while translating your prompt, please try again.')
449
+ return None, gr.Markdown('error')
450
+ break
451
+
452
+ shape = (rect_img.shape[1], rect_img.shape[0])
453
+ render_list, flag = parse_render_list(box_list, shape, box_num)
454
+ if flag:
455
+ render_img = render_all_text(render_list, shape)
456
+ else:
457
+ return None, gr.Markdown('error')
458
+
459
+ model_name = match_dict[base_model]
460
+ render_img = canny(np.array(render_img))
461
+
462
+ w, h = render_img.size
463
+ global pipeline, pre_pipeline
464
+
465
+ if pre_pipeline != model_name or pipeline is None:
466
+ pre_pipeline = model_name
467
+ pipeline = load_pipeline(model_name, scheduler_name)
468
+
469
+ batch_render_img = [render_img for _ in range(num_samples)]
470
+ batch_prompt = [f'{usr_prompt}, {a_prompt}' for _ in range(num_samples)]
471
+ batch_n_prompt = [n_prompt for _ in range(num_samples)]
472
+
473
+ images = pipeline(
474
+ batch_prompt,
475
+ negative_prompt=batch_n_prompt,
476
+ image=batch_render_img,
477
+ controlnet_conditioning_scale=float(conditioning_scale),
478
+ guidance_scale=float(cfg_scale),
479
+ width=w,
480
+ height=h,
481
+ num_inference_steps=int(inference_steps),
482
+ ).images
483
+
484
+ return images, gr.Markdown(f'{seed}, {usr_prompt}, {box_list}')
485
+
486
+
487
+ def draw_example(box_list, color, id):
488
+ board = Image.fromarray(create_canvas())
489
+
490
+ w, h = board.size
491
+
492
+ draw = ImageDraw.Draw(board, mode='RGBA')
493
+ visible = box_list[:BBOX_MAX_NUM]
494
+ pos = box_list[BBOX_MAX_NUM: 5 * BBOX_MAX_NUM]
495
+ font = box_list[5 * BBOX_MAX_NUM: 6 * BBOX_MAX_NUM]
496
+ text = box_list[6 * BBOX_MAX_NUM:]
497
+
498
+ info = {
499
+ 'visible': list(visible),
500
+ 'pos': list(pos),
501
+ 'font': list(font),
502
+ 'text': list(text)
503
+ }
504
+
505
+ with open(f'templates/{id}.json', 'w') as f:
506
+ json.dump(info, f)
507
+
508
+ for i in range(BBOX_MAX_NUM):
509
+ if visible[i] is True:
510
+ polygon = pos[i * 4: (i + 1) * 4]
511
+ print(polygon)
512
+ left = w * polygon[0]
513
+ top = h * polygon[1]
514
+ right = left + w * polygon[2]
515
+ bottom = top + h * polygon[3]
516
+ draw.rectangle([left, top, right, bottom], outline=color[i][0], fill=color[i][1], width=3)
517
+
518
+ board.save(f'./examples/{id}.png')
519
+
520
+
521
+ if __name__ == '__main__':
522
+ pass
javascript/bboxHint.js ADDED
@@ -0,0 +1,550 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Part of the implementation is borrowed and modified from multidiffusion-upscaler-for-automatic1111,
3
+ publicly available at https://github.com/pkuliyi2015/multidiffusion-upscaler-for-automatic1111
4
+ */
5
+
6
+ const BBOX_MAX_NUM = 16;
7
+ const BBOX_WARNING_SIZE = 1280;
8
+ const DEFAULT_X = 0.4;
9
+ const DEFAULT_Y = 0.4;
10
+ const DEFAULT_H = 0.2;
11
+ const DEFAULT_W = 0.2;
12
+
13
+ // ref: https://html-color.codes/
14
+ // 每个框对应的颜色
15
+ const COLOR_MAP = [
16
+ ['#ff0000', 'rgba(255, 0, 0, 0.3)'], // red
17
+ ['#ff9900', 'rgba(255, 153, 0, 0.3)'], // orange
18
+ ['#996633', 'rgba(153, 102, 51, 0.3)'], // brown
19
+ ['#33cc33', 'rgba(51, 204, 51, 0.3)'], // green
20
+ ['#33cccc', 'rgba(51, 204, 204, 0.3)'], // indigo
21
+ ['#0066ff', 'rgba(0, 102, 255, 0.3)'], // blue
22
+ ['#ff3399', 'rgba(255, 51, 153, 0.3)'], // hot pink
23
+ ['#cc00cc', 'rgba(204, 0, 204, 0.3)'], // dark pink
24
+ ['#ff6666', 'rgba(255, 102, 102, 0.3)'], // light red
25
+ ['#ffcc66', 'rgba(255, 204, 102, 0.3)'], // light orange
26
+ ['#99cc00', 'rgba(153, 204, 0, 0.3)'], // lime green
27
+ ['#ffff00', 'rgba(255, 255, 0, 0.3)'], // yellow
28
+ ['#0099cc', 'rgba(0, 153, 204, 0.3)'], // steel blue
29
+ ['#00cc99', 'rgba(0, 204, 153, 0.3)'], // teal
30
+ ['#ff3399', 'rgba(255, 51, 153, 0.3)'], // hot pink
31
+ ['#9933cc', 'rgba(153, 51, 204, 0.3)'], // lavender
32
+ ['#6600ff', 'rgba(102, 0, 255, 0.3)'], // purple
33
+ ];
34
+
35
+ const RESIZE_BORDER = 5;
36
+ const ROTATE_BORDER = 8;
37
+ const MOVE_BORDER = 5;
38
+
39
+ const t2i_bboxes = new Array(BBOX_MAX_NUM).fill(null);
40
+
41
+ function gradioApp() {
42
+ const elems = document.getElementsByTagName('gradio-app')
43
+ const gradioShadowRoot = elems.length == 0 ? null : elems[0].shadowRoot
44
+ return !!gradioShadowRoot ? gradioShadowRoot : document;
45
+ }
46
+
47
+ // ↓↓↓ called from gradio ↓↓↓
48
+ function onCreateT2IRefClick(overwrite) {
49
+ let width, height;
50
+ if (overwrite) {
51
+ const overwriteInputs = gradioApp().querySelectorAll('#MD-overwrite-width-t2i input, #MD-overwrite-height-t2i input');
52
+ width = parseInt(overwriteInputs[0].value);
53
+ height = parseInt(overwriteInputs[2].value);
54
+ } else {
55
+ const sizeInputs = gradioApp().querySelectorAll('#txt2img_width input, #txt2img_height input');
56
+ width = parseInt(sizeInputs[0].value);
57
+ height = parseInt(sizeInputs[2].value);
58
+ }
59
+
60
+ if (isNaN(width)) width = 512;
61
+ if (isNaN(height)) height = 512;
62
+
63
+ // Concat it to string to bypass the gradio bug
64
+ // 向黑恶势力低头
65
+ return width.toString() + 'x' + height.toString();
66
+ }
67
+
68
+ function onBoxEnableClick(idx, enable) {
69
+ let canvas = null;
70
+ let bboxes = null;
71
+ let locator = null;
72
+
73
+ // locator = () => gradioApp().querySelector('#MD-bbox-ref-t2i');
74
+ locator = () => gradioApp().querySelector('#MD-bbox-rect-t2i');
75
+ bboxes = t2i_bboxes;
76
+
77
+ ref_div = locator();
78
+ canvas = ref_div.querySelector('img');
79
+ if (!canvas) { return false; }
80
+
81
+ if (enable) {
82
+ // Check if the bounding box already exists
83
+ if (!bboxes[idx]) {
84
+ // Initialize bounding box
85
+ const bbox = [DEFAULT_X, DEFAULT_Y, DEFAULT_W, DEFAULT_H];
86
+ const colorMap = COLOR_MAP[idx % COLOR_MAP.length];
87
+ const div = document.createElement('div');
88
+ div.id = 'MD-bbox-t2i' + idx;
89
+ div.style.left = '0px';
90
+ div.style.top = '0px';
91
+ div.style.width = '0px';
92
+ div.style.height = '0px';
93
+ div.style.position = 'absolute';
94
+ div.style.border = '2px solid ' + colorMap[0];
95
+ div.style.background = colorMap[1];
96
+ div.style.zIndex = '900';
97
+ div.style.display = 'none';
98
+ // A text tip to warn the user if bbox is too large
99
+ const tip = document.createElement('span');
100
+ tip.id = 'MD-tip-t2i' + idx;
101
+ tip.style.left = '50%';
102
+ tip.style.top = '50%';
103
+ tip.style.position = 'absolute';
104
+ tip.style.transform = 'translate(-50%, -50%)';
105
+ tip.style.fontSize = '12px';
106
+ tip.style.fontWeight = 'bold';
107
+ tip.style.textAlign = 'center';
108
+ tip.style.color = colorMap[0];
109
+ tip.style.zIndex = '901';
110
+ tip.style.display = 'none';
111
+ tip.innerHTML = 'Warning: Region very large!<br>Take care of VRAM usage!';
112
+ div.appendChild(tip);
113
+ div.addEventListener('mousedown', function (e) {
114
+ if (e.button === 0) { onBoxMouseDown(e, idx); }
115
+ });
116
+ div.addEventListener('mousemove', function (e) {
117
+ updateCursorStyle(e, idx);
118
+ });
119
+
120
+ const shower = function() { // insert to DOM if necessary
121
+ if (!gradioApp().querySelector('#' + div.id)) {
122
+ locator().appendChild(div);
123
+ }
124
+ }
125
+ bboxes[idx] = [div, bbox, shower];
126
+ }
127
+
128
+ // Show the bounding box
129
+ console.log('Here')
130
+ console.log(canvas)
131
+ console.log
132
+ displayBox(canvas, bboxes[idx]);
133
+ return true;
134
+ } else {
135
+ if (!bboxes[idx]) { return false; }
136
+ const [div, bbox, shower] = bboxes[idx];
137
+ div.style.display = 'none';
138
+ }
139
+ return false;
140
+ }
141
+
142
+ function onBoxChange(idx, what, v) {
143
+ // This function handles all the changes of the bounding box
144
+ // Including the rendering and python slider update
145
+ let bboxes = null;
146
+ let canvas = null;
147
+
148
+ bboxes = t2i_bboxes;
149
+ canvas = gradioApp().querySelector('#MD-bbox-rect-t2i img');
150
+
151
+ if (!bboxes[idx] || !canvas) {
152
+ switch (what) {
153
+ case 'x': return DEFAULT_X;
154
+ case 'y': return DEFAULT_Y;
155
+ case 'w': return DEFAULT_W;
156
+ case 'h': return DEFAULT_H;
157
+ }
158
+ }
159
+ const [div, bbox, shower] = bboxes[idx];
160
+ if (div.style.display === 'none') { return v; }
161
+
162
+ // parse trigger
163
+ switch (what) {
164
+ case 'x': bbox[0] = v; break;
165
+ case 'y': bbox[1] = v; break;
166
+ case 'w': bbox[2] = v; break;
167
+ case 'h': bbox[3] = v; break;
168
+ }
169
+ displayBox(canvas, bboxes[idx]);
170
+ return v;
171
+ }
172
+
173
+ // ↓↓↓ called from js ↓↓↓
174
+ function getSeedInfo(id, current_seed) {
175
+ const info_id = '#html_info_txt2img';
176
+ const info_div = gradioApp().querySelector(info_id);
177
+ try{
178
+ current_seed = parseInt(current_seed);
179
+ } catch(e) {
180
+ current_seed = -1;
181
+ }
182
+ if (!info_div) return current_seed;
183
+ let info = info_div.innerHTML;
184
+ if (!info) return current_seed;
185
+ // remove all html tags
186
+ info = info.replace(/<[^>]*>/g, '');
187
+ // Find a json string 'region control:' in the info
188
+ // get its index
189
+ idx = info.indexOf('Region control');
190
+ if (idx == -1) return current_seed;
191
+ // get the json string (detect the bracket)
192
+ // find the first '{'
193
+ let start_idx = info.indexOf('{', idx);
194
+ let bracket = 1;
195
+ let end_idx = start_idx + 1;
196
+ while (bracket > 0 && end_idx < info.length) {
197
+ if (info[end_idx] == '{') bracket++;
198
+ if (info[end_idx] == '}') bracket--;
199
+ end_idx++;
200
+ }
201
+ if (bracket > 0) {
202
+ return current_seed;
203
+ }
204
+ // get the json string
205
+ let json_str = info.substring(start_idx, end_idx);
206
+ // replace the single quote to double quote
207
+ json_str = json_str.replace(/'/g, '"');
208
+ // replace python True to javascript true, False to false
209
+ json_str = json_str.replace(/True/g, 'true');
210
+ // parse the json string
211
+ let json = JSON.parse(json_str);
212
+ // get the seed if the region id is in the json
213
+ const region_id = 'Region ' + id.toString();
214
+ if (!(region_id in json)) return current_seed;
215
+ const region = json[region_id];
216
+ if (!('seed' in region)) return current_seed;
217
+ let seed = region['seed'];
218
+ try{
219
+ seed = parseInt(seed);
220
+ } catch(e) {
221
+ return current_seed;
222
+ }
223
+ return seed;
224
+ }
225
+
226
+ function displayBox(canvas, bbox_info) {
227
+ // check null input
228
+ const [div, bbox, shower] = bbox_info;
229
+ const [x, y, w, h] = bbox;
230
+ if (!canvas || !div || x == null || y == null || w == null || h == null) { return; }
231
+
232
+ // client: canvas widget display size
233
+ // natural: content image real size
234
+ let vpScale = Math.min(canvas.clientWidth / canvas.naturalWidth, canvas.clientHeight / canvas.naturalHeight);
235
+ let canvasCenterX = canvas.clientWidth / 2;
236
+ let canvasCenterY = canvas.clientHeight / 2;
237
+ let scaledX = canvas.naturalWidth * vpScale;
238
+ let scaledY = canvas.naturalHeight * vpScale;
239
+ let viewRectLeft = canvasCenterX - scaledX / 2;
240
+ let viewRectRight = canvasCenterX + scaledX / 2;
241
+ let viewRectTop = canvasCenterY - scaledY / 2;
242
+ let viewRectDown = canvasCenterY + scaledY / 2;
243
+
244
+ let xDiv = viewRectLeft + scaledX * x;
245
+ let yDiv = viewRectTop + scaledY * y;
246
+ let wDiv = Math.min(scaledX * w, viewRectRight - xDiv);
247
+ let hDiv = Math.min(scaledY * h, viewRectDown - yDiv);
248
+
249
+ // Calculate warning bbox size
250
+ let upscalerFactor = 1.0;
251
+ let maxSize = BBOX_WARNING_SIZE / upscalerFactor * vpScale;
252
+ let maxW = maxSize / scaledX;
253
+ let maxH = maxSize / scaledY;
254
+ if (w > maxW || h > maxH) {
255
+ div.querySelector('span').style.display = 'block';
256
+ } else {
257
+ div.querySelector('span').style.display = 'none';
258
+ }
259
+
260
+ // update <div> when not equal
261
+ div.style.left = xDiv + 'px';
262
+ div.style.top = yDiv + 'px';
263
+ div.style.width = wDiv + 'px';
264
+ div.style.height = hDiv + 'px';
265
+ div.style.display = 'block';
266
+
267
+ // insert it to DOM if not appear
268
+ shower();
269
+ }
270
+
271
+ function onBoxMouseDown(e, idx) {
272
+ let bboxes = null;
273
+ let canvas = null;
274
+
275
+ bboxes = t2i_bboxes;
276
+ canvas = gradioApp().querySelector('#MD-bbox-rect-t2i img');
277
+
278
+ // Get the bounding box
279
+ if (!canvas || !bboxes[idx]) { return; }
280
+ const [div, bbox, shower] = bboxes[idx];
281
+
282
+ // Check if the click is inside the bounding box
283
+ const boxRect = div.getBoundingClientRect();
284
+ let mouseX = e.clientX;
285
+ let mouseY = e.clientY;
286
+
287
+ const resizeLeft = mouseX >= boxRect.left && mouseX <= boxRect.left + RESIZE_BORDER;
288
+ const resizeRight = mouseX >= boxRect.right - RESIZE_BORDER && mouseX <= boxRect.right;
289
+ const resizeTop = mouseY >= boxRect.top && mouseY <= boxRect.top + RESIZE_BORDER;
290
+ const resizeBottom = mouseY >= boxRect.bottom - RESIZE_BORDER && mouseY <= boxRect.bottom;
291
+
292
+ const moveHorizontal = mouseX >= boxRect.left + MOVE_BORDER && mouseX <= boxRect.right - MOVE_BORDER;
293
+ const moveVertical = mouseY >= boxRect.top + MOVE_BORDER && mouseY <= boxRect.bottom - MOVE_BORDER;
294
+
295
+ if (!resizeLeft && !resizeRight && !resizeTop && !resizeBottom && !moveHorizontal && !moveVertical) { return; }
296
+
297
+ const horizontalPivot = resizeLeft ? bbox[0] + bbox[2] : bbox[0];
298
+ const verticalPivot = resizeTop ? bbox[1] + bbox[3] : bbox[1];
299
+
300
+ // Canvas can be regarded as invariant during the drag operation
301
+ // Calculate in advance to reduce overhead
302
+
303
+ // Calculate viewport scale based on the current canvas size and the natural image size
304
+ let vpScale = Math.min(canvas.clientWidth / canvas.naturalWidth, canvas.clientHeight / canvas.naturalHeight);
305
+ let vpOffset = canvas.getBoundingClientRect();
306
+
307
+ // Calculate scaled dimensions of the canvas
308
+ let scaledX = canvas.naturalWidth * vpScale;
309
+ let scaledY = canvas.naturalHeight * vpScale;
310
+
311
+ // Calculate the canvas center and view rectangle coordinates
312
+ let canvasCenterX = (vpOffset.left + window.scrollX) + canvas.clientWidth / 2;
313
+ let canvasCenterY = (vpOffset.top + window.scrollY) + canvas.clientHeight / 2;
314
+ let viewRectLeft = canvasCenterX - scaledX / 2 - window.scrollX;
315
+ let viewRectRight = canvasCenterX + scaledX / 2 - window.scrollX;
316
+ let viewRectTop = canvasCenterY - scaledY / 2 - window.scrollY;
317
+ let viewRectDown = canvasCenterY + scaledY / 2 - window.scrollY;
318
+
319
+ mouseX = Math.min(Math.max(mouseX, viewRectLeft), viewRectRight);
320
+ mouseY = Math.min(Math.max(mouseY, viewRectTop), viewRectDown);
321
+
322
+ const accordion = gradioApp().querySelector('#MD-tab-t2i');
323
+
324
+ // Move or resize the bounding box on mousemove
325
+ function onMouseMove(e) {
326
+ // Prevent selecting anything irrelevant
327
+ e.preventDefault();
328
+
329
+ // Get the new mouse position
330
+ let newMouseX = e.clientX;
331
+ let newMouseY = e.clientY;
332
+
333
+ // clamp the mouse position to the view rectangle
334
+ newMouseX = Math.min(Math.max(newMouseX, viewRectLeft), viewRectRight);
335
+ newMouseY = Math.min(Math.max(newMouseY, viewRectTop), viewRectDown);
336
+
337
+ // Calculate the mouse movement delta
338
+ const dx = (newMouseX - mouseX) / scaledX;
339
+ const dy = (newMouseY - mouseY) / scaledY;
340
+
341
+ // Update the mouse position
342
+ mouseX = newMouseX;
343
+ mouseY = newMouseY;
344
+
345
+ // if no move just return
346
+ if (dx === 0 && dy === 0) { return; }
347
+
348
+ // Update the mouse position
349
+ let [x, y, w, h] = bbox;
350
+ if (moveHorizontal && moveVertical) {
351
+ // If moving the bounding box
352
+ x = Math.min(Math.max(x + dx, 0), 1 - w);
353
+ y = Math.min(Math.max(y + dy, 0), 1 - h);
354
+ } else {
355
+ // If resizing the bounding box
356
+ if (resizeLeft || resizeRight) {
357
+ if (x < horizontalPivot) {
358
+ if (dx <= w) {
359
+ // If still within the left side of the pivot
360
+ x = x + dx;
361
+ w = w - dx;
362
+ } else {
363
+ // If crossing the pivot
364
+ w = dx - w;
365
+ x = horizontalPivot;
366
+ }
367
+ } else {
368
+ if (w + dx < 0) {
369
+ // If still within the right side of the pivot
370
+ x = horizontalPivot + w + dx;
371
+ w = - dx - w;
372
+ } else {
373
+ // If crossing the pivot
374
+ x = horizontalPivot;
375
+ w = w + dx;
376
+ }
377
+ }
378
+
379
+ // Clamp the bounding box to the image
380
+ if (x < 0) {
381
+ w = w + x;
382
+ x = 0;
383
+ } else if (x + w > 1) {
384
+ w = 1 - x;
385
+ }
386
+ }
387
+ // Same as above, but for the vertical axis
388
+ if (resizeTop || resizeBottom) {
389
+ if (y < verticalPivot) {
390
+ if (dy <= h) {
391
+ y = y + dy;
392
+ h = h - dy;
393
+ } else {
394
+ h = dy - h;
395
+ y = verticalPivot;
396
+ }
397
+ } else {
398
+ if (h + dy < 0) {
399
+ y = verticalPivot + h + dy;
400
+ h = - dy - h;
401
+ } else {
402
+ y = verticalPivot;
403
+ h = h + dy;
404
+ }
405
+ }
406
+ if (y < 0) {
407
+ h = h + y;
408
+ y = 0;
409
+ } else if (y + h > 1) {
410
+ h = 1 - y;
411
+ }
412
+ }
413
+ }
414
+ const [div, old_bbox, _] = bboxes[idx];
415
+
416
+ // If all the values are the same, just return
417
+ if (old_bbox[0] === x && old_bbox[1] === y && old_bbox[2] === w && old_bbox[3] === h) { return; }
418
+ // else update the bbox
419
+ const event = new Event('input');
420
+ const coords = [x, y, w, h];
421
+ // <del>The querySelector is not very efficient, so we query it once and reuse it</del>
422
+ // caching will result gradio bugs that stucks bbox and cannot move & drag
423
+ const sliderIds = ['x', 'y', 'w', 'h'];
424
+ // We try to select the input sliders
425
+ const sliderSelectors = sliderIds.map(id => `#MD-${'t2i'}-${idx}-${id} input`).join(', ');
426
+ let sliderInputs = accordion.querySelectorAll(sliderSelectors);
427
+ // alert(sliderInputs.length)
428
+ if (sliderInputs.length == 0) {
429
+ // If we failed, the accordion is probably closed and sliders are removed in the dom, so we open it
430
+ accordion.querySelector('.label-wrap').click();
431
+ // and try again
432
+ sliderInputs = accordion.querySelectorAll(sliderSelectors);
433
+ // If we still failed, we just return
434
+ if (sliderInputs.length == 0) { return; }
435
+ }
436
+ for (let i = 0; i < 4; i++) {
437
+ if (old_bbox[i] !== coords[i]) {
438
+ sliderInputs[2*i].value = coords[i];
439
+ sliderInputs[2*i].dispatchEvent(event);
440
+ }
441
+ }
442
+ }
443
+
444
+ // Remove the mousemove and mouseup event listeners
445
+ function onMouseUp() {
446
+ document.removeEventListener('mousemove', onMouseMove);
447
+ document.removeEventListener('mouseup', onMouseUp);
448
+ }
449
+
450
+ // Add the event listeners
451
+ document.addEventListener('mousemove', onMouseMove);
452
+ document.addEventListener('mouseup', onMouseUp);
453
+ }
454
+
455
+ function updateCursorStyle(e, idx) {
456
+ // This function changes the cursor style when hovering over the bounding box
457
+ const bboxes = t2i_bboxes;
458
+ if (!bboxes[idx]) return;
459
+
460
+ const div = bboxes[idx][0];
461
+ const boxRect = div.getBoundingClientRect();
462
+ const mouseX = e.clientX;
463
+ const mouseY = e.clientY;
464
+
465
+ const boxCenterX = boxRect.left + boxRect.width / 2;
466
+ const rotateAreaLeft = boxCenterX - ROTATE_BORDER;
467
+ const rotateAreaRight = boxCenterX + ROTATE_BORDER;
468
+ const rotateAreaTop = boxRect.top + ROTATE_BORDER;
469
+ const rotateAreaBottom = boxRect.top - ROTATE_BORDER;
470
+
471
+ const resizeLeft = mouseX >= boxRect.left && mouseX <= boxRect.left + RESIZE_BORDER;
472
+ const resizeRight = mouseX >= boxRect.right - RESIZE_BORDER && mouseX <= boxRect.right;
473
+ const resizeTop = mouseY >= boxRect.top && mouseY <= boxRect.top + RESIZE_BORDER;
474
+ const resizeBottom = mouseY >= boxRect.bottom - RESIZE_BORDER && mouseY <= boxRect.bottom;
475
+ const rotateTop = mouseX >= rotateAreaLeft && mouseX <= rotateAreaRight && mouseY >= rotateAreaBottom && mouseY <= rotateAreaTop; //mouseX >= rotateAreaLeft && mouseX <= rotateAreaRight &&
476
+
477
+
478
+ // if (rotateTop) {
479
+ // div.style.cursor = 'crosshair';
480
+ // } else
481
+ if ((resizeLeft && resizeTop) || (resizeRight && resizeBottom)) {
482
+ div.style.cursor = 'nwse-resize';
483
+ } else if ((resizeLeft && resizeBottom) || (resizeRight && resizeTop)) {
484
+ div.style.cursor = 'nesw-resize';
485
+ } else if (resizeLeft || resizeRight) {
486
+ div.style.cursor = 'ew-resize';
487
+ } else if (resizeTop || resizeBottom) {
488
+ div.style.cursor = 'ns-resize';
489
+ } else {
490
+ div.style.cursor = 'move';
491
+ }
492
+ }
493
+
494
+ // ↓↓↓ auto called event listeners ↓↓↓
495
+
496
+ function updateBoxes() {
497
+ // This function redraw all bounding boxes
498
+ let bboxes = null;
499
+ let canvas = null;
500
+
501
+ bboxes = t2i_bboxes;
502
+ canvas = gradioApp().querySelector('#MD-bbox-rect-t2i img');
503
+
504
+ if (!canvas) return;
505
+
506
+ for (let idx = 0; idx < bboxes.length; idx++) {
507
+ if (!bboxes[idx]) continue;
508
+ const [div, bbox, shower] = bboxes[idx];
509
+ if (div.style.display === 'none') { return; }
510
+
511
+ displayBox(canvas, bboxes[idx]);
512
+ }
513
+ }
514
+
515
+ window.addEventListener('resize', _ => {
516
+ updateBoxes(true);
517
+ updateBoxes(false);
518
+ });
519
+
520
+ //// ======== Gradio Bug Fix ========
521
+ //// For Gradio versions > 3.16.0 and < 3.29.0, the accordion DOM will be deleted when it is closed.
522
+ //// We need to judge the versions and listen to the accordion open event, rerender the bbox at that time.
523
+ //// This silly bug fix is only for compatibility, we recommend to update the gradio version to 3.29.0 or higher.
524
+ //try {
525
+ // const GRADIO_VERSIONS = window.gradio_config["version"].split(".");
526
+ // const gradio_major_version = parseInt(GRADIO_VERSIONS[0]);
527
+ // const gradio_minor_version = parseInt(GRADIO_VERSIONS[1]);
528
+ // if (gradio_major_version == 3 && gradio_minor_version > 16 && gradio_minor_version < 29) {
529
+ // let listener = e => {
530
+ // if (!e) { return; }
531
+ // if (!e.target) { return; }
532
+ // if (!e.target.classList) { return; }
533
+ // if (!e.target.classList.contains('label-wrap')) { return; }
534
+ // for (let tab of ['t2i']) {
535
+ // const div = gradioApp().querySelector('#MD-bbox-control-' + tab +' div.label-wrap');
536
+ // if (!div) { continue; }
537
+ // updateBoxes(tab === 't2i');
538
+ // }
539
+ // };
540
+ // window.addEventListener('DOMNodeInserted', listener);
541
+ // }
542
+ //} catch (ignored) {
543
+ // // If the above code failed, the gradio version shouldn't be in the range of 3.16.0 to 3.29.0, so we just return.
544
+ //}
545
+ //// ======== Gradio Bug Fix ========
546
+
547
+ //// 由于python程序中的所有bbox都绑定了点击事件,并且在点击后才会在前端中渲染,所以需要提前在js中初始化指定数量的bbox
548
+ //for (let i = 0; i < 4; i++) {
549
+ // onBoxEnableClick(i, true)
550
+ //}
requirements.txt ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==2.1.0
2
+ accelerate==0.31.0
3
+ addict==2.4.0
4
+ aiofiles==23.2.1
5
+ aiohttp==3.9.5
6
+ aiosignal==1.3.1
7
+ aliyun-python-sdk-core==2.15.1
8
+ aliyun-python-sdk-kms==2.16.3
9
+ altair==5.3.0
10
+ annotated-types==0.7.0
11
+ anyio==4.4.0
12
+ async-timeout==4.0.3
13
+ attrs==23.2.0
14
+ blinker==1.8.2
15
+ boto3==1.34.125
16
+ botocore==1.34.125
17
+ cachetools==5.3.3
18
+ certifi==2024.6.2
19
+ cffi==1.16.0
20
+ charset-normalizer==3.3.2
21
+ click==8.1.7
22
+ colorama==0.4.6
23
+ config==0.5.1
24
+ contourpy==1.2.1
25
+ controlnet-aux==0.0.9
26
+ crcmod==1.7
27
+ cryptography==42.0.8
28
+ cycler==0.12.1
29
+ datasets==2.18.0
30
+ diffusers==0.28.2
31
+ dill==0.3.8
32
+ distro==1.9.0
33
+ dnspython==2.6.1
34
+ einops==0.8.0
35
+ email_validator==2.1.1
36
+ et-xmlfile==1.1.0
37
+ exceptiongroup==1.2.1
38
+ fastapi==0.111.0
39
+ fastapi-cli==0.0.4
40
+ ffmpy==0.3.2
41
+ filelock==3.14.0
42
+ Flask==3.0.3
43
+ flatbuffers==24.3.25
44
+ fonttools==4.53.0
45
+ frozenlist==1.4.1
46
+ fsspec==2024.2.0
47
+ gast==0.5.4
48
+ gradio==3.50.0
49
+ gradio_client==0.6.1
50
+ h11==0.14.0
51
+ httpcore==1.0.5
52
+ httptools==0.6.1
53
+ httpx==0.27.0
54
+ huggingface-hub==0.23.2
55
+ idna==3.7
56
+ imageio==2.34.1
57
+ importlib_metadata==7.1.0
58
+ importlib_resources==6.4.0
59
+ intel-openmp==2021.4.0
60
+ itsdangerous==2.2.0
61
+ jax==0.4.30
62
+ jaxlib==0.4.30
63
+ Jinja2==3.1.4
64
+ jmespath==0.10.0
65
+ jsonschema==4.22.0
66
+ jsonschema-specifications==2023.12.1
67
+ kiwisolver==1.4.5
68
+ lazy_loader==0.4
69
+ lightning-utilities==0.11.2
70
+ linkify-it-py==2.0.3
71
+ markdown-it-py==2.2.0
72
+ MarkupSafe==2.1.5
73
+ matplotlib==3.9.0
74
+ mdit-py-plugins==0.3.3
75
+ mdurl==0.1.2
76
+ mediapipe==0.10.14
77
+ mkl==2021.4.0
78
+ ml-dtypes==0.4.0
79
+ modelscope==1.15.0
80
+ mpmath==1.3.0
81
+ multidict==6.0.5
82
+ multiprocess==0.70.16
83
+ networkx==3.3
84
+ numpy==1.26.4
85
+ openai==1.35.3
86
+ opencv-contrib-python==4.10.0.84
87
+ opencv-python==4.10.0.82
88
+ opencv-python-headless==4.10.0.82
89
+ openpyxl==3.1.5
90
+ opt-einsum==3.3.0
91
+ orjson==3.10.3
92
+ oss2==2.18.5
93
+ packaging==24.0
94
+ pandas==2.2.2
95
+ pillow==10.3.0
96
+ platformdirs==4.2.2
97
+ protobuf==4.25.3
98
+ psutil==5.9.8
99
+ pyarrow==16.1.0
100
+ pyarrow-hotfix==0.6
101
+ pycparser==2.22
102
+ pycryptodome==3.20.0
103
+ pydantic==2.7.3
104
+ pydantic_core==2.18.4
105
+ pydub==0.25.1
106
+ Pygments==2.18.0
107
+ PyJWT==2.8.0
108
+ pyparsing==3.1.2
109
+ python-dateutil==2.9.0.post0
110
+ python-dotenv==1.0.1
111
+ python-multipart==0.0.9
112
+ pytorch-lightning==2.3.0
113
+ pytz==2024.1
114
+ PyYAML==6.0.1
115
+ referencing==0.35.1
116
+ regex==2024.5.15
117
+ requests==2.32.3
118
+ rich==13.7.1
119
+ rpds-py==0.18.1
120
+ ruff==0.4.7
121
+ s3transfer==0.10.1
122
+ safetensors==0.4.3
123
+ scikit-image==0.23.2
124
+ scipy==1.13.1
125
+ semantic-version==2.10.0
126
+ shellingham==1.5.4
127
+ simplejson==3.19.2
128
+ six==1.16.0
129
+ sniffio==1.3.1
130
+ sortedcontainers==2.4.0
131
+ sounddevice==0.4.7
132
+ starlette==0.37.2
133
+ sympy==1.12.1
134
+ tbb==2021.12.0
135
+ tifffile==2024.5.22
136
+ timm==0.6.7
137
+ tokenizers==0.19.1
138
+ tomli==2.0.1
139
+ tomlkit==0.12.0
140
+ toolz==0.12.1
141
+ torch==2.3.1
142
+ torchmetrics==1.4.0.post0
143
+ torchvision==0.18.1
144
+ tqdm==4.66.4
145
+ transformers==4.41.2
146
+ typer==0.12.3
147
+ typing_extensions==4.12.1
148
+ tzdata==2024.1
149
+ uc-micro-py==1.0.3
150
+ ujson==5.10.0
151
+ urllib3==2.2.1
152
+ uvicorn==0.30.1
153
+ watchfiles==0.22.0
154
+ websockets==11.0.3
155
+ Werkzeug==3.0.3
156
+ xxhash==3.4.1
157
+ yapf==0.40.2
158
+ yarl==1.9.4
159
+ zhipuai==2.1.1.20240620.1
160
+ zipp==3.19.2
templates/1.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"visible": [true, true, true, false, false, false, false, false], "pos": [0.024161073825503594, 0.028187919463086675, 0.6295302013422815, 0.14966442953020198, 0.11476510067114097, 0.18187919463087276, 0.6060402684563758, 0.1395973154362417, 0.8268456375838927, 0.10134228187919483, 0.15973154362416148, 0.8140939597315439, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2], "font": ["CHN-\u5df4\u8700\u58a8\u8ff9", "CHN-\u5df4\u8700\u58a8\u8ff9", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977"], "text": ["\u5929\u7136\u6c27\u5427", "\u767b\u9ad8\u671b\u8fdc", "\u4fc3\u8fdb\u5faa\u73af \u5f3a\u5065\u8eab\u4f53", "", "", "", "", ""]}
templates/1.png ADDED
templates/2.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"visible": [true, true, false, false, false, false, false, false], "pos": [0.1885906040268458, 0.8302013422818793, 0.6496644295302016, 0.13624161073825505, 0.2026845637583891, 0.6590604026845638, 0.6161073825503363, 0.15302013422818816, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2], "font": ["RUS-Caveat", "CHN-\u6e05\u677e\u624b\u5199\u4f53", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977"], "text": ["\u043b\u0435\u043a\u0430\u0440\u0441\u0442\u0432", "\u89c4\u5f8b\u670d\u836f", "", "", "", "", "", ""]}
templates/2.png ADDED
templates/3.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"visible": [true, true, true, false, false, false, false, false], "pos": [0.13825503355704757, 0.0476510067114097, 0.2838926174496645, 0.11946308724832223, 0.43422818791946327, 0.05100671140939635, 0.44832214765100653, 0.11946308724832207, 0.1382550335570471, 0.19865771812080546, 0.7402684563758394, 0.12281879194630875, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2], "font": ["CHN-\u6e05\u677e\u624b\u5199\u4f53", "JPN-GlTsukiji", "ENG-Filthyrich", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977"], "text": ["\u4f60\u597d", "\u3053\u3093\u306b\u3061\u306f", "It's showtime", "", "", "", "", ""]}
templates/3.png ADDED
templates/4.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"visible": [true, true, true, false, false, false, false, false], "pos": [0.5449664429530201, 0.05436241610738259, 0.4281879194630869, 0.15973154362416114, 0.43355704697986586, 0.7959731543624167, 0.5489932885906045, 0.18657718120805372, 0.024161073825503712, 0.037583892617449835, 0.20335570469798664, 0.46845637583892613, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2], "font": ["RUS-Alice", "CHN-\u6f14\u793a\u590f\u884c\u6977", "KOR-ChosunGs", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977"], "text": ["\u0412\u043e\u0434\u043a\u0430", "\u8bf7\u9002\u91cf\u996e\u9152", "\uce90\uc8fc\uc5bc \ubc14", "", "", "", "", ""]}
templates/4.png ADDED
templates/5.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"visible": [true, true, false, false, false, false, false, false], "pos": [0.3201342281879193, 0.017449664429530044, 0.364429530201342, 0.14966442953020143, 0.13825503355704727, 0.19194630872483293, 0.74026845637584, 0.13624161073825514, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2], "font": ["CHN-\u6e05\u677e\u624b\u5199\u4f53", "JPN-Aoyagireisyosimo", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977"], "text": ["\u73ab\u7470", "\u6c17\u3092\u6674\u3089\u3057\u307e\u3059", "", "", "", "", "", ""]}
templates/5.png ADDED
templates/6.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"visible": [true, true, true, true, false, false, false, false], "pos": [0.010738255033557227, 0.23892617449664466, 0.6060402684563759, 0.12617449664429542, 0.06107382550335593, 0.383892617449664, 0.3409395973154365, 0.10268456375838927, 0.06442953020134237, 0.500671140939596, 0.3375838926174498, 0.09597315436241619, 0.4637583892617447, 0.8637583892617449, 0.5053691275167783, 0.11946308724832194, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2, 0.4, 0.4, 0.2, 0.2], "font": ["CHN-\u6e05\u677e\u624b\u5199\u4f53", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "ENG-Okesip", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977", "CHN-\u534e\u6587\u884c\u6977"], "text": ["\u71ac\u591c\u6709\u5bb3\u5065\u5eb7", "\u4f11\u606f\u65e9", "\u7cbe\u795e\u597d", "Good Night", "", "", "", ""]}
templates/6.png ADDED