Fataj commited on
Commit
e84f79c
·
verified ·
1 Parent(s): e71d988

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -793
app.py DELETED
@@ -1,793 +0,0 @@
1
- import json
2
- import os
3
- import random
4
- import subprocess
5
- import sys
6
- import threading
7
- import time
8
- import uuid
9
- from pathlib import Path
10
-
11
- import gradio as gr
12
- import requests
13
- import spaces
14
- from huggingface_hub import hf_hub_download
15
- from PIL import Image
16
-
17
-
18
- MODEL_REPO = "Daankular/redcraft-krea2-fp8"
19
- MODEL_FILE = "redcraftKREA2RedMix_krea2Edition.safetensors"
20
- COMFY_KREA_REPO = "Comfy-Org/Krea-2"
21
- TEXT_ENCODER_FILE = "qwen3vl_4b_bf16.safetensors"
22
- VAE_FILE = "qwen_image_vae.safetensors"
23
- IDENTITY_LORA_REPO = "conradlocke/krea2-identity-edit"
24
- IDENTITY_LORA_FILE = "krea2_identity_edit_v1_2.safetensors"
25
- KREA2EDIT_NODE_REPO = "https://github.com/lbouaraba/comfyui-krea2edit"
26
- KREA2EDIT_NODE_DIRNAME = "comfyui-krea2edit"
27
- COMFY_REPO = "https://github.com/comfyanonymous/ComfyUI.git"
28
- COMFY_DIR = Path(os.environ.get("COMFYUI_DIR", "/tmp/ComfyUI"))
29
- COMFY_HOST = "127.0.0.1"
30
- COMFY_PORT = int(os.environ.get("COMFYUI_PORT", "8188"))
31
- COMFY_URL = f"http://{COMFY_HOST}:{COMFY_PORT}"
32
- MAX_SEED = 2**31 - 1
33
-
34
- _comfy_lock = threading.Lock()
35
- _comfy_process = None
36
-
37
-
38
- def _run(cmd, cwd=None):
39
- print("[setup]", " ".join(map(str, cmd)), flush=True)
40
- subprocess.check_call(cmd, cwd=str(cwd) if cwd else None)
41
-
42
-
43
- def _wait_for_comfy(timeout=180):
44
- deadline = time.time() + timeout
45
- last_error = None
46
- while time.time() < deadline:
47
- try:
48
- response = requests.get(f"{COMFY_URL}/system_stats", timeout=2)
49
- if response.ok:
50
- return
51
- except Exception as exc:
52
- last_error = exc
53
- time.sleep(1)
54
- raise RuntimeError(f"ComfyUI did not start in time: {last_error}")
55
-
56
-
57
- def _validate_comfyui():
58
- response = requests.get(f"{COMFY_URL}/object_info", timeout=30)
59
- response.raise_for_status()
60
- object_info = response.json()
61
- required_nodes = ["UNETLoader", "CLIPLoader", "VAELoader", "CLIPTextEncode", "KSampler", "VAEDecode", "SaveImage"]
62
- missing = [node for node in required_nodes if node not in object_info]
63
- if missing:
64
- raise RuntimeError(f"ComfyUI is missing required nodes: {', '.join(missing)}")
65
-
66
- unet_info = object_info["UNETLoader"]["input"]["required"]["unet_name"][0]
67
- clip_info = object_info["CLIPLoader"]["input"]["required"]["clip_name"][0]
68
- vae_info = object_info["VAELoader"]["input"]["required"]["vae_name"][0]
69
- if MODEL_FILE not in unet_info:
70
- raise RuntimeError(f"Redcraft diffusion model is not visible to ComfyUI. First models: {', '.join(unet_info[:10])}")
71
- if TEXT_ENCODER_FILE not in clip_info:
72
- raise RuntimeError(f"Krea2 text encoder is not visible to ComfyUI. First encoders: {', '.join(clip_info[:10])}")
73
- if VAE_FILE not in vae_info:
74
- raise RuntimeError(f"Krea2 VAE is not visible to ComfyUI. First VAEs: {', '.join(vae_info[:10])}")
75
-
76
- identity_edit_nodes = ["LoraLoaderModelOnly", "Krea2EditModelPatch", "Krea2EditGroundedEncode", "EmptySD3LatentImage"]
77
- missing_identity_nodes = [node for node in identity_edit_nodes if node not in object_info]
78
- if missing_identity_nodes:
79
- raise RuntimeError(
80
- f"ComfyUI-Krea2Edit nodes are missing: {', '.join(missing_identity_nodes)}. "
81
- f"Check the {KREA2EDIT_NODE_REPO} custom node install."
82
- )
83
- lora_info = object_info["LoraLoaderModelOnly"]["input"]["required"]["lora_name"][0]
84
- if IDENTITY_LORA_FILE not in lora_info:
85
- raise RuntimeError(f"Identity-edit LoRA is not visible to ComfyUI. First loras: {', '.join(lora_info[:10])}")
86
-
87
-
88
- def _ensure_comfyui():
89
- global _comfy_process
90
-
91
- with _comfy_lock:
92
- if _comfy_process is not None and _comfy_process.poll() is None:
93
- return
94
- try:
95
- response = requests.get(f"{COMFY_URL}/system_stats", timeout=2)
96
- if response.ok:
97
- return
98
- except Exception:
99
- pass
100
-
101
- if not COMFY_DIR.exists():
102
- _run(["git", "clone", "--depth", "1", COMFY_REPO, str(COMFY_DIR)])
103
-
104
- marker = COMFY_DIR / ".requirements-installed"
105
- if not marker.exists():
106
- _run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], cwd=COMFY_DIR)
107
- marker.write_text("ok", encoding="utf-8")
108
-
109
- krea2edit_dir = COMFY_DIR / "custom_nodes" / KREA2EDIT_NODE_DIRNAME
110
- if not krea2edit_dir.exists():
111
- _run(["git", "clone", "--depth", "1", KREA2EDIT_NODE_REPO, str(krea2edit_dir)])
112
-
113
- diffusion_dir = COMFY_DIR / "models" / "diffusion_models"
114
- text_encoder_dir = COMFY_DIR / "models" / "text_encoders"
115
- vae_dir = COMFY_DIR / "models" / "vae"
116
- lora_dir = COMFY_DIR / "models" / "loras"
117
- diffusion_dir.mkdir(parents=True, exist_ok=True)
118
- text_encoder_dir.mkdir(parents=True, exist_ok=True)
119
- vae_dir.mkdir(parents=True, exist_ok=True)
120
- lora_dir.mkdir(parents=True, exist_ok=True)
121
- hf_hub_download(
122
- repo_id=MODEL_REPO,
123
- filename=MODEL_FILE,
124
- local_dir=str(diffusion_dir),
125
- token=os.environ.get("HF_TOKEN"),
126
- )
127
- hf_hub_download(
128
- repo_id=COMFY_KREA_REPO,
129
- filename=f"text_encoders/{TEXT_ENCODER_FILE}",
130
- local_dir=str(COMFY_DIR / "models"),
131
- token=os.environ.get("HF_TOKEN"),
132
- )
133
- hf_hub_download(
134
- repo_id=COMFY_KREA_REPO,
135
- filename=f"vae/{VAE_FILE}",
136
- local_dir=str(COMFY_DIR / "models"),
137
- token=os.environ.get("HF_TOKEN"),
138
- )
139
- hf_hub_download(
140
- repo_id=IDENTITY_LORA_REPO,
141
- filename=IDENTITY_LORA_FILE,
142
- local_dir=str(lora_dir),
143
- token=os.environ.get("HF_TOKEN"),
144
- )
145
-
146
-
147
- def _start_comfyui():
148
- global _comfy_process
149
-
150
- with _comfy_lock:
151
- if _comfy_process is not None and _comfy_process.poll() is None:
152
- return
153
- try:
154
- response = requests.get(f"{COMFY_URL}/system_stats", timeout=2)
155
- if response.ok:
156
- _validate_comfyui()
157
- return
158
- except Exception:
159
- pass
160
-
161
- cmd = [
162
- sys.executable,
163
- "main.py",
164
- "--listen",
165
- COMFY_HOST,
166
- "--port",
167
- str(COMFY_PORT),
168
- "--disable-auto-launch",
169
- ]
170
- _comfy_process = subprocess.Popen(cmd, cwd=str(COMFY_DIR))
171
- _wait_for_comfy()
172
- _validate_comfyui()
173
-
174
-
175
- def _build_workflow(prompt, negative_prompt, width, height, steps, cfg, seed, sampler, scheduler):
176
- workflow = {
177
- "1": {
178
- "class_type": "UNETLoader",
179
- "inputs": {"unet_name": MODEL_FILE, "weight_dtype": "default"},
180
- },
181
- "8": {
182
- "class_type": "CLIPLoader",
183
- "inputs": {"clip_name": TEXT_ENCODER_FILE, "type": "krea2", "device": "default"},
184
- },
185
- "9": {
186
- "class_type": "VAELoader",
187
- "inputs": {"vae_name": VAE_FILE},
188
- },
189
- "2": {
190
- "class_type": "CLIPTextEncode",
191
- "inputs": {"text": prompt, "clip": ["8", 0]},
192
- },
193
- "4": {
194
- "class_type": "EmptyLatentImage",
195
- "inputs": {"width": int(width), "height": int(height), "batch_size": 1},
196
- },
197
- "5": {
198
- "class_type": "KSampler",
199
- "inputs": {
200
- "seed": int(seed),
201
- "steps": int(steps),
202
- "cfg": float(cfg),
203
- "sampler_name": sampler,
204
- "scheduler": scheduler,
205
- "denoise": 1.0,
206
- "model": ["1", 0],
207
- "positive": ["2", 0],
208
- "negative": ["3", 0],
209
- "latent_image": ["4", 0],
210
- },
211
- },
212
- "6": {
213
- "class_type": "VAEDecode",
214
- "inputs": {"samples": ["5", 0], "vae": ["9", 0]},
215
- },
216
- "7": {
217
- "class_type": "SaveImage",
218
- "inputs": {"filename_prefix": "redcraft", "images": ["6", 0]},
219
- },
220
- }
221
-
222
- if negative_prompt and negative_prompt.strip():
223
- workflow["3"] = {
224
- "class_type": "CLIPTextEncode",
225
- "inputs": {"text": negative_prompt, "clip": ["8", 0]},
226
- }
227
- else:
228
- workflow["3"] = {
229
- "class_type": "ConditioningZeroOut",
230
- "inputs": {"conditioning": ["2", 0]},
231
- }
232
- return workflow
233
-
234
-
235
- def _upload_image_to_comfy(image):
236
- if image is None:
237
- raise ValueError("Upload an image to edit.")
238
-
239
- image = image.convert("RGB")
240
- filename = f"redcraft-input-{uuid.uuid4().hex}.png"
241
- temp_path = Path("/tmp") / filename
242
- image.save(temp_path)
243
- with temp_path.open("rb") as handle:
244
- response = requests.post(
245
- f"{COMFY_URL}/upload/image",
246
- files={"image": (filename, handle, "image/png")},
247
- data={"overwrite": "true"},
248
- timeout=120,
249
- )
250
- response.raise_for_status()
251
- data = response.json()
252
- return data.get("name", filename)
253
-
254
-
255
- def _resize_for_edit(image, width, height):
256
- width, height = int(width), int(height)
257
- if width <= 0 or height <= 0:
258
- return image.convert("RGB")
259
- return image.convert("RGB").resize((width, height), Image.LANCZOS)
260
-
261
-
262
- def _target_size_from_source(image, max_megapixels=1.0):
263
- multiple = 16
264
- width, height = image.size
265
- megapixels = (width * height) / 1_000_000
266
- if megapixels > max_megapixels:
267
- scale = (max_megapixels / megapixels) ** 0.5
268
- width, height = round(width * scale), round(height * scale)
269
- width = max(multiple, (width // multiple) * multiple)
270
- height = max(multiple, (height // multiple) * multiple)
271
- return width, height
272
-
273
-
274
- def _build_edit_workflow(input_filename, prompt, negative_prompt, steps, cfg, seed, sampler, scheduler, denoise):
275
- workflow = {
276
- "1": {
277
- "class_type": "UNETLoader",
278
- "inputs": {"unet_name": MODEL_FILE, "weight_dtype": "default"},
279
- },
280
- "8": {
281
- "class_type": "CLIPLoader",
282
- "inputs": {"clip_name": TEXT_ENCODER_FILE, "type": "krea2", "device": "default"},
283
- },
284
- "9": {
285
- "class_type": "VAELoader",
286
- "inputs": {"vae_name": VAE_FILE},
287
- },
288
- "10": {
289
- "class_type": "LoadImage",
290
- "inputs": {"image": input_filename},
291
- },
292
- "11": {
293
- "class_type": "VAEEncode",
294
- "inputs": {"pixels": ["10", 0], "vae": ["9", 0]},
295
- },
296
- "2": {
297
- "class_type": "CLIPTextEncode",
298
- "inputs": {"text": prompt, "clip": ["8", 0]},
299
- },
300
- "5": {
301
- "class_type": "KSampler",
302
- "inputs": {
303
- "seed": int(seed),
304
- "steps": int(steps),
305
- "cfg": float(cfg),
306
- "sampler_name": sampler,
307
- "scheduler": scheduler,
308
- "denoise": float(denoise),
309
- "model": ["1", 0],
310
- "positive": ["2", 0],
311
- "negative": ["3", 0],
312
- "latent_image": ["11", 0],
313
- },
314
- },
315
- "6": {
316
- "class_type": "VAEDecode",
317
- "inputs": {"samples": ["5", 0], "vae": ["9", 0]},
318
- },
319
- "7": {
320
- "class_type": "SaveImage",
321
- "inputs": {"filename_prefix": "redcraft-edit", "images": ["6", 0]},
322
- },
323
- }
324
-
325
- if negative_prompt and negative_prompt.strip():
326
- workflow["3"] = {
327
- "class_type": "CLIPTextEncode",
328
- "inputs": {"text": negative_prompt, "clip": ["8", 0]},
329
- }
330
- else:
331
- workflow["3"] = {
332
- "class_type": "ConditioningZeroOut",
333
- "inputs": {"conditioning": ["2", 0]},
334
- }
335
- return workflow
336
-
337
-
338
- def _build_identity_edit_workflow(input_filename, prompt, width, height, steps, cfg, seed, ref_boost, grounding_px, sampler, scheduler):
339
- return {
340
- "1": {
341
- "class_type": "UNETLoader",
342
- "inputs": {"unet_name": MODEL_FILE, "weight_dtype": "default"},
343
- },
344
- "8": {
345
- "class_type": "CLIPLoader",
346
- "inputs": {"clip_name": TEXT_ENCODER_FILE, "type": "krea2", "device": "default"},
347
- },
348
- "9": {
349
- "class_type": "VAELoader",
350
- "inputs": {"vae_name": VAE_FILE},
351
- },
352
- "20": {
353
- "class_type": "LoraLoaderModelOnly",
354
- "inputs": {"lora_name": IDENTITY_LORA_FILE, "strength_model": 1.0, "model": ["1", 0]},
355
- },
356
- "10": {
357
- "class_type": "LoadImage",
358
- "inputs": {"image": input_filename},
359
- },
360
- "11": {
361
- "class_type": "VAEEncode",
362
- "inputs": {"pixels": ["10", 0], "vae": ["9", 0]},
363
- },
364
- "21": {
365
- "class_type": "Krea2EditModelPatch",
366
- "inputs": {
367
- "model": ["20", 0],
368
- "source_latent": ["11", 0],
369
- "ref_boost": float(ref_boost),
370
- "fit_mode": "fit",
371
- "vae": ["9", 0],
372
- "source_image": ["10", 0],
373
- },
374
- },
375
- "2": {
376
- "class_type": "Krea2EditGroundedEncode",
377
- "inputs": {"clip": ["8", 0], "prompt": prompt, "image": ["10", 0], "grounding_px": int(grounding_px)},
378
- },
379
- "3": {
380
- "class_type": "Krea2EditGroundedEncode",
381
- "inputs": {"clip": ["8", 0], "prompt": "", "image": ["10", 0], "grounding_px": int(grounding_px)},
382
- },
383
- "4": {
384
- "class_type": "EmptySD3LatentImage",
385
- "inputs": {"width": int(width), "height": int(height), "batch_size": 1},
386
- },
387
- "5": {
388
- "class_type": "KSampler",
389
- "inputs": {
390
- "seed": int(seed),
391
- "steps": int(steps),
392
- "cfg": float(cfg),
393
- "sampler_name": sampler,
394
- "scheduler": scheduler,
395
- "denoise": 1.0,
396
- "model": ["21", 0],
397
- "positive": ["2", 0],
398
- "negative": ["3", 0],
399
- "latent_image": ["4", 0],
400
- },
401
- },
402
- "6": {
403
- "class_type": "VAEDecode",
404
- "inputs": {"samples": ["5", 0], "vae": ["9", 0]},
405
- },
406
- "7": {
407
- "class_type": "SaveImage",
408
- "inputs": {"filename_prefix": "identity-edit", "images": ["6", 0]},
409
- },
410
- }
411
-
412
-
413
- def _queue_prompt(workflow):
414
- payload = {"prompt": workflow, "client_id": str(uuid.uuid4())}
415
- response = requests.post(f"{COMFY_URL}/prompt", json=payload, timeout=30)
416
- if not response.ok:
417
- raise RuntimeError(f"ComfyUI prompt error {response.status_code}: {response.text[:1000]}")
418
- return response.json()["prompt_id"]
419
-
420
-
421
- def _wait_for_history(prompt_id, timeout=900):
422
- deadline = time.time() + timeout
423
- while time.time() < deadline:
424
- response = requests.get(f"{COMFY_URL}/history/{prompt_id}", timeout=30)
425
- response.raise_for_status()
426
- history = response.json()
427
- if prompt_id in history:
428
- item = history[prompt_id]
429
- status = item.get("status", {})
430
- if status.get("completed"):
431
- return item
432
- messages = status.get("messages") or []
433
- for message in messages:
434
- if isinstance(message, list) and message and message[0] == "execution_error":
435
- raise RuntimeError(json.dumps(message[1], indent=2)[:2000])
436
- time.sleep(1)
437
- raise RuntimeError("Timed out waiting for ComfyUI generation.")
438
-
439
-
440
- def _load_output_image(history_item):
441
- outputs = history_item.get("outputs", {})
442
- for output in outputs.values():
443
- for image in output.get("images", []):
444
- params = {
445
- "filename": image["filename"],
446
- "subfolder": image.get("subfolder", ""),
447
- "type": image.get("type", "output"),
448
- }
449
- response = requests.get(f"{COMFY_URL}/view", params=params, timeout=120)
450
- response.raise_for_status()
451
- temp_path = Path("/tmp") / f"{uuid.uuid4().hex}.png"
452
- temp_path.write_bytes(response.content)
453
- return Image.open(temp_path).convert("RGB")
454
- raise RuntimeError("ComfyUI completed without returning an image.")
455
-
456
-
457
- def _duration(prompt, negative_prompt, width, height, steps, cfg, seed, randomize_seed, sampler, scheduler):
458
- megapixels = max(1.0, (int(width) * int(height)) / (1024 * 1024))
459
- return int(900 + int(steps) * 8 * megapixels)
460
-
461
-
462
- def _edit_duration(
463
- input_image,
464
- prompt,
465
- negative_prompt,
466
- width,
467
- height,
468
- steps,
469
- cfg,
470
- denoise,
471
- seed,
472
- randomize_seed,
473
- sampler,
474
- scheduler,
475
- ):
476
- megapixels = max(1.0, (int(width) * int(height)) / (1024 * 1024))
477
- return int(900 + int(steps) * 9 * megapixels)
478
-
479
-
480
- def _identity_edit_duration(
481
- input_image,
482
- prompt,
483
- ref_boost,
484
- grounding_px,
485
- max_megapixels,
486
- steps,
487
- cfg,
488
- seed,
489
- randomize_seed,
490
- sampler,
491
- scheduler,
492
- ):
493
- return int(900 + int(steps) * 10 * max(1.0, float(max_megapixels)))
494
-
495
-
496
- @spaces.GPU(duration=_duration)
497
- def generate(
498
- prompt,
499
- negative_prompt="",
500
- width=1024,
501
- height=1024,
502
- steps=10,
503
- cfg=1.0,
504
- seed=0,
505
- randomize_seed=True,
506
- sampler="er_sde",
507
- scheduler="simple",
508
- ):
509
- if not prompt or not prompt.strip():
510
- raise gr.Error("Enter a prompt.")
511
- if randomize_seed:
512
- seed = random.randint(0, MAX_SEED)
513
-
514
- try:
515
- _start_comfyui()
516
- workflow = _build_workflow(prompt, negative_prompt, width, height, steps, cfg, seed, sampler, scheduler)
517
- prompt_id = _queue_prompt(workflow)
518
- history_item = _wait_for_history(prompt_id)
519
- return _load_output_image(history_item), seed
520
- except Exception as exc:
521
- raise gr.Error(str(exc)) from exc
522
-
523
-
524
- @spaces.GPU(duration=_edit_duration)
525
- def edit_image(
526
- input_image,
527
- prompt,
528
- negative_prompt="",
529
- width=1024,
530
- height=1024,
531
- steps=12,
532
- cfg=1.2,
533
- denoise=0.35,
534
- seed=0,
535
- randomize_seed=True,
536
- sampler="er_sde",
537
- scheduler="simple",
538
- ):
539
- if input_image is None:
540
- raise gr.Error("Upload an image to edit.")
541
- if not prompt or not prompt.strip():
542
- raise gr.Error("Enter an edit prompt.")
543
- if randomize_seed:
544
- seed = random.randint(0, MAX_SEED)
545
-
546
- try:
547
- _start_comfyui()
548
- resized = _resize_for_edit(input_image, width, height)
549
- input_filename = _upload_image_to_comfy(resized)
550
- workflow = _build_edit_workflow(
551
- input_filename,
552
- prompt,
553
- negative_prompt,
554
- steps,
555
- cfg,
556
- seed,
557
- sampler,
558
- scheduler,
559
- denoise,
560
- )
561
- prompt_id = _queue_prompt(workflow)
562
- history_item = _wait_for_history(prompt_id)
563
- return _load_output_image(history_item), seed
564
- except Exception as exc:
565
- raise gr.Error(str(exc)) from exc
566
-
567
-
568
- @spaces.GPU(duration=_identity_edit_duration)
569
- def identity_edit(
570
- input_image,
571
- prompt,
572
- ref_boost=4.0,
573
- grounding_px=768,
574
- max_megapixels=1.0,
575
- steps=10,
576
- cfg=1.0,
577
- seed=0,
578
- randomize_seed=True,
579
- sampler="euler",
580
- scheduler="simple",
581
- ):
582
- if input_image is None:
583
- raise gr.Error("Upload an image to edit.")
584
- if not prompt or not prompt.strip():
585
- raise gr.Error("Enter an edit instruction.")
586
- if randomize_seed:
587
- seed = random.randint(0, MAX_SEED)
588
-
589
- try:
590
- _start_comfyui()
591
- source = input_image.convert("RGB")
592
- width, height = _target_size_from_source(source, max_megapixels)
593
- input_filename = _upload_image_to_comfy(source)
594
- workflow = _build_identity_edit_workflow(
595
- input_filename,
596
- prompt,
597
- width,
598
- height,
599
- steps,
600
- cfg,
601
- seed,
602
- ref_boost,
603
- grounding_px,
604
- sampler,
605
- scheduler,
606
- )
607
- prompt_id = _queue_prompt(workflow)
608
- history_item = _wait_for_history(prompt_id)
609
- return _load_output_image(history_item), seed
610
- except Exception as exc:
611
- raise gr.Error(str(exc)) from exc
612
-
613
-
614
- CSS = """
615
- .gradio-container { max-width: 1120px !important; margin: 0 auto !important; }
616
- #result-image { min-height: 520px; }
617
- """
618
-
619
- with gr.Blocks(title="Redcraft Krea2", css=CSS) as demo:
620
- gr.Markdown("# Redcraft Krea2")
621
- gr.Markdown("ComfyUI-native Redcraft Krea2 generation and image editing.")
622
-
623
- with gr.Tabs():
624
- with gr.Tab("Generate"):
625
- with gr.Row():
626
- with gr.Column(scale=5):
627
- prompt = gr.Textbox(label="Prompt", lines=5, placeholder="Describe the image to generate.")
628
- negative_prompt = gr.Textbox(label="Negative prompt", lines=2, value="")
629
- with gr.Row():
630
- width = gr.Slider(512, 1536, value=1024, step=64, label="Width")
631
- height = gr.Slider(512, 1536, value=1024, step=64, label="Height")
632
- with gr.Row():
633
- steps = gr.Slider(1, 30, value=10, step=1, label="Steps")
634
- cfg = gr.Slider(0.0, 8.0, value=1.0, step=0.1, label="CFG")
635
- with gr.Row():
636
- seed = gr.Slider(0, MAX_SEED, value=0, step=1, label="Seed")
637
- randomize_seed = gr.Checkbox(value=True, label="Randomize seed")
638
- with gr.Accordion("Sampler", open=False):
639
- sampler = gr.Dropdown(
640
- ["er_sde", "euler", "euler_ancestral", "dpmpp_2m", "dpmpp_sde"],
641
- value="er_sde",
642
- label="Sampler",
643
- )
644
- scheduler = gr.Dropdown(["simple", "normal", "karras", "exponential"], value="simple", label="Scheduler")
645
- run = gr.Button("Generate", variant="primary")
646
- with gr.Column(scale=6):
647
- output = gr.Image(label="Result", format="png", elem_id="result-image")
648
-
649
- inputs = [prompt, negative_prompt, width, height, steps, cfg, seed, randomize_seed, sampler, scheduler]
650
- run.click(generate, inputs, [output, seed])
651
- prompt.submit(generate, inputs, [output, seed])
652
-
653
- with gr.Tab("Edit Image"):
654
- with gr.Row():
655
- with gr.Column(scale=5):
656
- edit_input = gr.Image(type="pil", label="Input image")
657
- edit_prompt = gr.Textbox(label="Edit prompt", lines=5, placeholder="Describe the edit while preserving identity.")
658
- edit_negative_prompt = gr.Textbox(label="Negative prompt", lines=2, value="")
659
- with gr.Row():
660
- edit_width = gr.Slider(512, 1536, value=1024, step=64, label="Width")
661
- edit_height = gr.Slider(512, 1536, value=1024, step=64, label="Height")
662
- with gr.Row():
663
- edit_steps = gr.Slider(1, 30, value=12, step=1, label="Steps")
664
- edit_cfg = gr.Slider(0.0, 8.0, value=1.2, step=0.1, label="CFG")
665
- edit_denoise = gr.Slider(
666
- 0.05,
667
- 0.8,
668
- value=0.35,
669
- step=0.05,
670
- label="Edit strength",
671
- info="Lower values preserve identity and composition more strongly.",
672
- )
673
- with gr.Row():
674
- edit_seed = gr.Slider(0, MAX_SEED, value=0, step=1, label="Seed")
675
- edit_randomize_seed = gr.Checkbox(value=True, label="Randomize seed")
676
- with gr.Accordion("Sampler", open=False):
677
- edit_sampler = gr.Dropdown(
678
- ["er_sde", "euler", "euler_ancestral", "dpmpp_2m", "dpmpp_sde"],
679
- value="er_sde",
680
- label="Sampler",
681
- )
682
- edit_scheduler = gr.Dropdown(["simple", "normal", "karras", "exponential"], value="simple", label="Scheduler")
683
- edit_run = gr.Button("Edit Image", variant="primary")
684
- with gr.Column(scale=6):
685
- edit_output = gr.Image(label="Edited image", format="png", elem_id="result-image")
686
-
687
- edit_inputs = [
688
- edit_input,
689
- edit_prompt,
690
- edit_negative_prompt,
691
- edit_width,
692
- edit_height,
693
- edit_steps,
694
- edit_cfg,
695
- edit_denoise,
696
- edit_seed,
697
- edit_randomize_seed,
698
- edit_sampler,
699
- edit_scheduler,
700
- ]
701
- edit_run.click(edit_image, edit_inputs, [edit_output, edit_seed])
702
- edit_prompt.submit(edit_image, edit_inputs, [edit_output, edit_seed])
703
-
704
- with gr.Tab("Identity Edit"):
705
- gr.Markdown(
706
- "Instruction-based, identity-preserving editing using the community LoRA "
707
- "[`conradlocke/krea2-identity-edit`](https://huggingface.co/conradlocke/krea2-identity-edit) "
708
- "on the Redcraft Krea2 checkpoint, via the "
709
- "[ComfyUI-Krea2Edit](https://github.com/lbouaraba/comfyui-krea2edit) node pack. "
710
- "Give it an image and a plain-language instruction; it edits while preserving what you "
711
- "didn't ask to change, including the person."
712
- )
713
- with gr.Row():
714
- with gr.Column(scale=5):
715
- id_input = gr.Image(type="pil", label="Source image")
716
- id_prompt = gr.Textbox(
717
- label="Edit instruction",
718
- lines=3,
719
- placeholder="e.g. create a photo of this person at a night market",
720
- )
721
- id_ref_boost = gr.Slider(
722
- 0.0,
723
- 10.0,
724
- value=4.0,
725
- step=0.5,
726
- label="Likeness (ref_boost)",
727
- info="How hard the edit holds the reference. 1 = off, 4 = strong likeness (recommended), 8+ over-copies.",
728
- )
729
- with gr.Accordion("Advanced settings", open=False):
730
- id_grounding_px = gr.Slider(
731
- 384,
732
- 1536,
733
- value=768,
734
- step=64,
735
- label="Grounding resolution (px)",
736
- info="Lower = stronger edit adherence. Higher = stronger identity likeness. Trained range 384-768; 1024+ often still works.",
737
- )
738
- id_max_mp = gr.Slider(0.5, 2.0, value=1.0, step=0.1, label="Output size (megapixels)")
739
- with gr.Row():
740
- id_steps = gr.Slider(4, 28, value=10, step=1, label="Steps")
741
- id_cfg = gr.Slider(
742
- 0.0,
743
- 5.0,
744
- value=1.0,
745
- step=0.5,
746
- label="CFG",
747
- info="Turbo convention: 1.0 = guidance off. Raise (e.g. 3) with more steps for removals/large edits.",
748
- )
749
- with gr.Row():
750
- id_seed = gr.Slider(0, MAX_SEED, value=0, step=1, label="Seed")
751
- id_randomize_seed = gr.Checkbox(value=True, label="Randomize seed")
752
- with gr.Row():
753
- id_sampler = gr.Dropdown(
754
- ["euler", "er_sde", "euler_ancestral", "dpmpp_2m", "dpmpp_sde"],
755
- value="euler",
756
- label="Sampler",
757
- )
758
- id_scheduler = gr.Dropdown(["simple", "normal", "karras", "exponential"], value="simple", label="Scheduler")
759
- id_run = gr.Button("Edit Identity", variant="primary")
760
- with gr.Column(scale=6):
761
- id_output = gr.Image(label="Edited image", format="png", elem_id="result-image")
762
-
763
- id_inputs = [
764
- id_input,
765
- id_prompt,
766
- id_ref_boost,
767
- id_grounding_px,
768
- id_max_mp,
769
- id_steps,
770
- id_cfg,
771
- id_seed,
772
- id_randomize_seed,
773
- id_sampler,
774
- id_scheduler,
775
- ]
776
- id_run.click(identity_edit, id_inputs, [id_output, id_seed])
777
- id_prompt.submit(identity_edit, id_inputs, [id_output, id_seed])
778
-
779
- gr.Examples(
780
- examples=[
781
- ["examples/woman.jpg", "create a photo of this person at a busy night market at night"],
782
- ["examples/businessman_suit.jpg", "change the suit jacket to a red leather jacket"],
783
- ["examples/man_beach.jpg", "make it a vintage film photo with warm golden-hour light"],
784
- ],
785
- inputs=[id_input, id_prompt],
786
- label="Examples",
787
- )
788
-
789
-
790
- _ensure_comfyui()
791
-
792
- if __name__ == "__main__":
793
- demo.queue().launch()