cocoat commited on
Commit
4d6b597
·
verified ·
1 Parent(s): 08711e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -89
app.py CHANGED
@@ -21,142 +21,239 @@ model_path = hf_hub_download(
21
  filename="recocoamixXL3_coamixXL3.safetensors"
22
  )
23
 
24
- # パイプライン構築&スケジューラ設定
25
  pipe = StableDiffusionXLPipeline.from_single_file(
26
- model_path, torch_dtype=torch_dtype, use_safetensors=True
 
 
27
  ).to(device)
28
- euler_scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config, use_karras_sigmas=True)
29
- dpm_scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
 
 
 
 
 
 
 
30
  pipe.scheduler = euler_scheduler
31
 
32
- # 生成履歴保持
33
  history = []
34
 
35
  # HTMLテーブル生成ヘルパー
36
  def make_html_table(caption):
37
  rows = caption.split("\n")
38
- html = '<table style="width:100%;border-collapse:collapse;background:#fffaf1;">'
39
  for row in rows:
40
  if ": " in row:
41
- k, v = row.split(": ", 1)
42
  html += (
43
- f'<tr><th style="text-align:left;border:1px solid #ddd;padding:8px;color:#8b5e3c;">{k}</th>'
44
- f'<td style="border:1px solid #ddd;padding:8px;">{v}</td></tr>'
45
  )
46
  html += '</table>'
47
  return html
48
 
49
  # 推論関数
50
- def infer(prompt, neg, seed, rand, w, h, cfg, steps, sched, progress=gr.Progress(track_tqdm=True)):
 
 
 
51
  if rand:
52
  seed = random.randint(0, MAX_SEED)
53
- gen = torch.Generator(device=device).manual_seed(seed)
54
- pipe.scheduler = euler_scheduler if sched == "Euler Ancestral" else dpm_scheduler
 
 
 
 
55
  pipe.scheduler.set_timesteps(steps)
56
 
57
- def cb(p, i, t, kw):
58
- progress(i/steps, desc=f"Step {i}/{steps}")
59
- return kw
60
 
61
- out = pipe(
62
  prompt=prompt,
63
  negative_prompt=neg or None,
64
  guidance_scale=cfg,
65
  num_inference_steps=steps,
66
- width=w, height=h,
67
- generator=gen,
68
- callback_on_step_end=cb
 
69
  )
70
- img = out.images[0]
71
- cap = (
 
 
72
  f"Prompt: {prompt}\n"
73
  f"Negative: {neg or 'None'}\n"
74
  f"Seed: {seed}\n"
75
  f"Size: {w}×{h}\n"
76
  f"CFG: {cfg}\n"
77
  f"Steps: {steps}\n"
78
- f"Scheduler: {sched}"
79
  )
80
- history.insert(0, (img, cap))
81
- progress(1.0, desc="Done!")
82
- gallery = [(im, make_html_table(caption)) for im, caption in history]
83
- return img, gallery
84
 
85
- # カフェ風スタイル + カスタムローダー用CSS
86
- css = r"""
87
- body { background:#f4e1c1; font-family:'Georgia', serif; color:#4b2e2e; }
88
- #col-container { background:#fffaf1; padding:20px; border-radius:16px;
89
- box-shadow:0 4px 12px rgba(0,0,0,0.1); margin:auto; max-width:780px; }
90
- #col-container a { color:#8b5e3c; text-decoration:none; border-bottom:1px dotted #8b5e3c; }
91
 
92
- /* デフォルトスピナーを完全非表示 */
93
- .loading-screen, .gradio-spinner, .gr-spinner, .loading { display: none!important; }
 
 
 
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  /* カスタムローダー */
96
  #custom-loader {
97
- display: none; position: fixed; top: 16px; right: 16px;
98
- padding: 8px 12px; background: rgba(255,255,255,0.95);
99
- border-radius: 8px; align-items: center; font-size: 16px;
100
- font-weight: bold; color: #734c36; z-index: 9999;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  }
102
- @keyframes fadeLetter { 0%,100% { opacity:1; } 50% { opacity:0.2; } }
103
- #custom-loader span { display:inline-block; animation:fadeLetter 1s ease-in-out infinite; }
104
  """
105
 
106
  with gr.Blocks(css=css) as demo:
107
- # カスタムローダー要素 + アニメーションテキスト + アイコン
108
- gr.HTML(r"""
109
- <div id="custom-loader">
110
- <span id="loader-text"></span>
111
- <img src="/icon.png" style="width:32px;height:32px;border-radius:50%;margin-left:8px;"/>
112
- </div>
113
- <script>
114
- (function(){
115
- const text = "in progress";
116
- const span = document.getElementById("loader-text");
117
- let idx = 0;
118
- function animate() {
119
- if (idx <= text.length) {
120
- span.textContent = text.slice(0, idx++);
121
- setTimeout(animate, 150);
122
- }
123
- }
124
- // ボタンクリックでローダー表示
125
- document.querySelector('button').addEventListener('click', () => {
126
- document.getElementById('custom-loader').style.display = 'flex';
127
- idx = 0; animate();
128
- });
129
- // 画像生成完了でローダー非表示
130
- const obs = new MutationObserver(() => {
131
- if (document.querySelector('.gr-gallery img')) {
132
- document.getElementById('custom-loader').style.display = 'none'; obs.disconnect();
133
- }
134
- });
135
- obs.observe(document.body, { childList:true, subtree:true });
136
- })();
137
- </script>
138
- """)
139
-
140
  with gr.Column(elem_id="col-container"):
141
  gr.Markdown("## SDXL Base – cocoamixXL3 Demo")
142
  gr.Markdown("[Link: Civitai](https://civitai.com/models/1553716?modelVersionId=1855218)")
143
- prompt = gr.Textbox(lines=1, placeholder="Prompt…", value="1girl, cocoart, masterpiece, anime,")
144
- neg = gr.Textbox(lines=1, placeholder="Negative prompt", value="low quality, worst quality,…")
145
- seed_sl = gr.Slider(0, MAX_SEED, step=1, value=0, label="Seed")
146
- rand_chk = gr.Checkbox(True, label="Randomize seed")
147
- width_sl = gr.Slider(256, MAX_SIZE, step=32, value=512, label="Width")
148
- height_sl = gr.Slider(256, MAX_SIZE, step=32, value=512, label="Height")
149
- cfg_sl = gr.Slider(1.0, 30.0, step=0.1, value=7.5, label="CFG Scale")
150
- steps_sl = gr.Slider(1, 50, step=1, value=12, label="Steps")
151
- sched_rad = gr.Radio(["Euler Ancestral","DPM++ 2M SDE"], value="Euler Ancestral", label="Scheduler")
152
- run_btn = gr.Button("Generate")
153
- img_out = gr.Image()
154
- history_gal = gr.Gallery(label="生成履歴", columns=4, height=280, interactive=True, type="pil")
155
-
156
- run_btn.click(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  fn=infer,
158
- inputs=[prompt, neg, seed_sl, rand_chk, width_sl, height_sl, cfg_sl, steps_sl, sched_rad],
159
- outputs=[img_out, history_gal]
160
  )
161
 
162
  demo.queue()
 
21
  filename="recocoamixXL3_coamixXL3.safetensors"
22
  )
23
 
24
+ # 単一ファイルからパイプラインを構築
25
  pipe = StableDiffusionXLPipeline.from_single_file(
26
+ model_path,
27
+ torch_dtype=torch_dtype,
28
+ use_safetensors=True
29
  ).to(device)
30
+
31
+ # スケジューラのプリセット
32
+ euler_scheduler = EulerAncestralDiscreteScheduler.from_config(
33
+ pipe.scheduler.config,
34
+ use_karras_sigmas=True
35
+ )
36
+ dpm_scheduler = DPMSolverMultistepScheduler.from_config(
37
+ pipe.scheduler.config
38
+ )
39
  pipe.scheduler = euler_scheduler
40
 
41
+ # 生成履歴を (PIL.Image, キャプションテキスト) タプルで保持
42
  history = []
43
 
44
  # HTMLテーブル生成ヘルパー
45
  def make_html_table(caption):
46
  rows = caption.split("\n")
47
+ html = '<table style="width:100%;border-collapse:collapse;">'
48
  for row in rows:
49
  if ": " in row:
50
+ key, val = row.split(": ", 1)
51
  html += (
52
+ f'<tr><th style="text-align:left;border:1px solid #ddd;padding:4px;">{key}</th>'
53
+ f'<td style="border:1px solid #ddd;padding:4px;">{val}</td></tr>'
54
  )
55
  html += '</table>'
56
  return html
57
 
58
  # 推論関数
59
+ def infer(
60
+ prompt, neg, seed, rand, w, h, cfg, steps, scheduler_type,
61
+ progress=gr.Progress(track_tqdm=True)
62
+ ):
63
  if rand:
64
  seed = random.randint(0, MAX_SEED)
65
+ generator = torch.Generator(device=device).manual_seed(seed)
66
+
67
+ # スケジューラ切替
68
+ pipe.scheduler = (
69
+ euler_scheduler if scheduler_type == "Euler Ancestral" else dpm_scheduler
70
+ )
71
  pipe.scheduler.set_timesteps(steps)
72
 
73
+ def _callback(pipeline, step_idx, timestep, callback_kwargs):
74
+ progress(step_idx / steps, desc=f"Step {step_idx}/{steps}")
75
+ return callback_kwargs
76
 
77
+ output = pipe(
78
  prompt=prompt,
79
  negative_prompt=neg or None,
80
  guidance_scale=cfg,
81
  num_inference_steps=steps,
82
+ width=w,
83
+ height=h,
84
+ generator=generator,
85
+ callback_on_step_end=_callback
86
  )
87
+ img = output.images[0]
88
+
89
+ # キャプション構築
90
+ caption_text = (
91
  f"Prompt: {prompt}\n"
92
  f"Negative: {neg or 'None'}\n"
93
  f"Seed: {seed}\n"
94
  f"Size: {w}×{h}\n"
95
  f"CFG: {cfg}\n"
96
  f"Steps: {steps}\n"
97
+ f"Scheduler: {scheduler_type}"
98
  )
 
 
 
 
99
 
100
+ # 履歴に追加(最新を先頭)
101
+ history.insert(0, (img, caption_text))
102
+ progress(1.0, desc="Done!")
 
 
 
103
 
104
+ # Gallery には (PIL.Image, HTML caption) のリストを渡す
105
+ gallery_items = [
106
+ (item[0], make_html_table(item[1])) for item in history
107
+ ]
108
+ return img, gallery_items
109
 
110
+ # CSS設定(温かい喫茶店風)
111
+ css = """
112
+ body {
113
+ background-color: #f4e1c1;
114
+ font-family: 'Georgia', serif;
115
+ }
116
+ #col-container {
117
+ background: #fffaf1;
118
+ padding: 20px;
119
+ border-radius: 16px;
120
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
121
+ margin: auto;
122
+ max-width: 780px;
123
+ }
124
+ /* リンクの装飾 */
125
+ #col-container a {
126
+ color: #8b5e3c;
127
+ text-decoration: none;
128
+ border-bottom: 1px dotted #8b5e3c;
129
+ }
130
+ /* ボタン */
131
+ .gr-button {
132
+ background-color: #d4a373 !important;
133
+ color: white !important;
134
+ border-radius: 8px !important;
135
+ padding: 10px 24px !important;
136
+ font-weight: bold;
137
+ transition: background-color 0.3s;
138
+ }
139
+ .gr-button:hover {
140
+ background-color: #c48f61 !important;
141
+ }
142
+ /* フォーム要素 */
143
+ .gr-textbox, .gr-slider, .gr-radio, .gr-checkbox, .gr-image {
144
+ background: #fff;
145
+ border-radius: 8px;
146
+ }
147
+ /* Gallery */
148
+ .gr-gallery {
149
+ background: #fffaf1;
150
+ padding: 10px;
151
+ border-radius: 12px;
152
+ }
153
+ /* デフォルトスピナーを隠す */
154
+ .gradio-spinner { display: none !important; }
155
  /* カスタムローダー */
156
  #custom-loader {
157
+ display: none;
158
+ align-items: center;
159
+ justify-content: center;
160
+ font-weight: bold;
161
+ margin: 12px 0;
162
+ }
163
+ body.gradio-running #custom-loader { display: flex; }
164
+ /* 文字1文字ずつのフェードアニメーション */
165
+ @keyframes fadeLetter {
166
+ 0%,100% { opacity: 1; }
167
+ 50% { opacity: 0.2; }
168
+ }
169
+ #custom-loader .loading-text span {
170
+ display: inline-block;
171
+ animation: fadeLetter 1s ease-in-out infinite;
172
+ }
173
+ /* アイコン */
174
+ #custom-loader img {
175
+ width: 32px;
176
+ height: 32px;
177
+ border-radius: 50%;
178
+ margin-left: 8px;
179
  }
 
 
180
  """
181
 
182
  with gr.Blocks(css=css) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  with gr.Column(elem_id="col-container"):
184
  gr.Markdown("## SDXL Base – cocoamixXL3 Demo")
185
  gr.Markdown("[Link: Civitai](https://civitai.com/models/1553716?modelVersionId=1855218)")
186
+
187
+ # フォーム配置(かわいくアレンジ)
188
+ with gr.Row():
189
+ prompt = gr.Textbox(
190
+ lines=1,
191
+ placeholder="Prompt…",
192
+ value="1girl, cocoart, masterpiece, anime,"
193
+ )
194
+ neg = gr.Textbox(
195
+ lines=1,
196
+ placeholder="Negative prompt",
197
+ value=(
198
+ "low quality, worst quality, bad shadow, lowres, error, miss stroke, "
199
+ "sketch art, smoke, ugly, extra digits, creepy, imprecise, glowing blur,"
200
+ )
201
+ )
202
+ with gr.Row():
203
+ seed_sl = gr.Slider(0, MAX_SEED, step=1, value=0, label="Seed")
204
+ rand = gr.Checkbox(True, label="Randomize seed")
205
+ with gr.Row():
206
+ width = gr.Slider(256, MAX_SIZE, step=32, value=512, label="Width")
207
+ height = gr.Slider(256, MAX_SIZE, step=32, value=512, label="Height")
208
+ with gr.Row():
209
+ cfg = gr.Slider(1.0, 30.0, step=0.1, value=7.5, label="CFG Scale")
210
+ steps = gr.Slider(1, 50, step=1, value=12, label="Steps")
211
+ with gr.Row():
212
+ scheduler_type = gr.Radio(
213
+ choices=["Euler Ancestral", "DPM++ 2M SDE"],
214
+ value="Euler Ancestral",
215
+ label="Scheduler"
216
+ )
217
+ run = gr.Button("Generate")
218
+
219
+ # カスタムローディング
220
+ loader = gr.HTML(
221
+ '<div id="custom-loader">'
222
+ ' <div class="loading-text">'
223
+ ' <span style="animation-delay:0s">i</span>'
224
+ ' <span style="animation-delay:0.1s">n</span>'
225
+ ' <span style="animation-delay:0.2s"> </span>'
226
+ ' <span style="animation-delay:0.3s">p</span>'
227
+ ' <span style="animation-delay:0.4s">r</span>'
228
+ ' <span style="animation-delay:0.5s">o</span>'
229
+ ' <span style="animation-delay:0.6s">g</span>'
230
+ ' <span style="animation-delay:0.7s">r</span>'
231
+ ' <span style="animation-delay:0.8s">e</span>'
232
+ ' <span style="animation-delay:0.9s">s</span>'
233
+ ' <span style="animation-delay:1.0s">s</span>'
234
+ ' </div>'
235
+ ' <img src="icon.png" alt="loading icon" />'
236
+ '</div>'
237
+ )
238
+
239
+ img_out = gr.Image()
240
+ state = gr.State([])
241
+
242
+ # 生成履歴ギャラリー(画像+キャプションHTML)
243
+ history_gallery = gr.Gallery(
244
+ label="生成履歴",
245
+ columns=4,
246
+ height=280,
247
+ show_label=False,
248
+ interactive=True,
249
+ type="pil"
250
+ )
251
+
252
+ # イベント設定
253
+ run.click(
254
  fn=infer,
255
+ inputs=[prompt, neg, seed_sl, rand, width, height, cfg, steps, scheduler_type],
256
+ outputs=[img_out, history_gallery]
257
  )
258
 
259
  demo.queue()