comrender commited on
Commit
1332b22
·
verified ·
1 Parent(s): d401044

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +270 -170
app.py CHANGED
@@ -8,91 +8,110 @@ import spaces
8
  import torch
9
  from gradio_imageslider import ImageSlider
10
  from PIL import Image
11
- import requests
12
- import sys
13
- import subprocess
14
  from huggingface_hub import hf_hub_download
 
 
15
  import tempfile
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- os.environ["GIT_TERMINAL_PROMPT"] = "0"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  # Setup ComfyUI and custom nodes
20
  if not os.path.exists("ComfyUI"):
21
- subprocess.run(["git", "clone", "https://github.com/comfyanonymous/ComfyUI"])
22
-
23
- custom_nodes_dir = os.path.join("ComfyUI", "custom_nodes")
24
- os.makedirs(custom_nodes_dir, exist_ok=True)
25
-
26
- # Clone UltimateSDUpscaler
27
- usd_dir = os.path.join(custom_nodes_dir, "ComfyUI_UltimateSDUpscaler")
28
- if not os.path.exists(usd_dir):
29
- subprocess.run(["git", "clone", "https://github.com/ssitu/ComfyUI_UltimateSDUpscaler", usd_dir])
30
-
31
- # Clone comfy_mtb
32
- mtb_dir = os.path.join(custom_nodes_dir, "comfy_mtb")
33
- if not os.path.exists(mtb_dir):
34
- subprocess.run(["git", "clone", "https://github.com/melMass/comfy_mtb", mtb_dir])
35
- # Install requirements
36
- if os.path.exists(os.path.join(mtb_dir, "requirements.txt")):
37
- subprocess.run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], cwd=mtb_dir)
38
-
39
- # Clone KJNodes
40
- kjn_dir = os.path.join(custom_nodes_dir, "ComfyUI-KJNodes")
41
- if not os.path.exists(kjn_dir):
42
- subprocess.run(["git", "clone", "https://github.com/kijai/ComfyUI-KJNodes", kjn_dir])
43
- # Install requirements
44
- if os.path.exists(os.path.join(kjn_dir, "requirements.txt")):
45
- subprocess.run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], cwd=kjn_dir)
46
 
47
  # Download models if not present
48
- comfy_models_dir = os.path.join("ComfyUI", "models")
49
- os.makedirs(comfy_models_dir, exist_ok=True)
50
-
51
- # Diffusion models (Flux FP8)
52
- diffusion_dir = os.path.join(comfy_models_dir, "diffusion_models")
53
- os.makedirs(diffusion_dir, exist_ok=True)
54
- if not os.path.exists(os.path.join(diffusion_dir, "flux1-dev-fp8.safetensors")):
55
- hf_hub_download(repo_id="Kijai/flux-fp8", filename="flux1-dev-fp8.safetensors", local_dir=diffusion_dir)
56
-
57
- # CLIP models
58
- clip_dir = os.path.join(comfy_models_dir, "clip")
59
- os.makedirs(clip_dir, exist_ok=True)
60
- if not os.path.exists(os.path.join(clip_dir, "clip_l.safetensors")):
61
- hf_hub_download(repo_id="comfyanonymous/flux_text_encoders", filename="clip_l.safetensors", local_dir=clip_dir)
62
- if not os.path.exists(os.path.join(clip_dir, "t5xxl_fp8_e4m3fn.safetensors")):
63
- hf_hub_download(repo_id="comfyanonymous/flux_text_encoders", filename="t5xxl_fp8_e4m3fn.safetensors", local_dir=clip_dir)
64
-
65
- # VAE
66
- vae_dir = os.path.join(comfy_models_dir, "vae")
67
- os.makedirs(vae_dir, exist_ok=True)
68
- if not os.path.exists(os.path.join(vae_dir, "ae.safetensors")):
69
- hf_hub_download(repo_id="black-forest-labs/FLUX.1-dev", filename="ae.safetensors", local_dir=vae_dir)
70
-
71
- # Upscale models
72
- upscale_dir = os.path.join(comfy_models_dir, "upscale_models")
73
- os.makedirs(upscale_dir, exist_ok=True)
74
- for model_name in ["RealESRGAN_x2.pth", "RealESRGAN_x4.pth"]:
75
- model_path = os.path.join(upscale_dir, model_name)
76
- if not os.path.exists(model_path):
77
- url = f"https://huggingface.co/ai-forever/Real-ESRGAN/resolve/main/{model_name}"
78
- with open(model_path, "wb") as f:
79
- f.write(requests.get(url).content)
80
-
81
- # Add ComfyUI to sys.path
82
- sys.path.append(os.path.abspath("ComfyUI"))
83
-
84
- # Import custom nodes
85
- from nodes import NODE_CLASS_MAPPINGS, init_custom_nodes
86
- init_custom_nodes()
87
-
88
- # From the provided script
89
- def get_value_at_index(obj, index):
90
- try:
91
- return obj[index]
92
- except KeyError:
93
- return obj["result"][index]
94
 
95
- # CSS and constants similar to original
96
  css = """
97
  #col-container {
98
  margin: 0 auto;
@@ -104,7 +123,6 @@ css = """
104
  }
105
  """
106
 
107
- power_device = "ZeroGPU"
108
  MAX_SEED = 1000000
109
  MAX_PIXEL_BUDGET = 8192 * 8192
110
 
@@ -118,7 +136,7 @@ def process_input(input_image, upscale_factor):
118
  was_resized = False
119
 
120
  if w * h * upscale_factor**2 > MAX_PIXEL_BUDGET:
121
- gr.Info("Requested output too large. Resizing input.")
122
  target_input_pixels = MAX_PIXEL_BUDGET / (upscale_factor ** 2)
123
  scale = (target_input_pixels / (w * h)) ** 0.5
124
  new_w = max(16, int(w * scale) // 16 * 16)
@@ -128,13 +146,19 @@ def process_input(input_image, upscale_factor):
128
 
129
  return input_image, w_original, h_original, was_resized
130
 
 
131
  def load_image_from_url(url):
132
  try:
133
  response = requests.get(url, stream=True)
134
  response.raise_for_status()
135
  return Image.open(response.raw)
136
  except Exception as e:
137
- raise gr.Error(f"Failed to load image: {e}")
 
 
 
 
 
138
 
139
  @spaces.GPU(duration=120)
140
  def enhance_image(
@@ -149,71 +173,73 @@ def enhance_image(
149
  tile_size,
150
  progress=gr.Progress(track_tqdm=True),
151
  ):
152
- with torch.inference_mode():
153
- # Handle input image
154
- if image_input is not None:
155
- true_input_image = image_input
156
- elif image_url:
157
- true_input_image = load_image_from_url(image_url)
158
- else:
159
- raise gr.Error("Provide an image or URL")
160
-
161
- input_image, w_original, h_original, was_resized = process_input(true_input_image, upscale_factor)
162
-
163
- if randomize_seed:
164
- seed = random.randint(0, MAX_SEED)
165
-
166
- # Prepare ComfyUI input image
167
- input_dir = os.path.join("ComfyUI", "input")
168
- os.makedirs(input_dir, exist_ok=True)
169
- temp_filename = f"input_{random.randint(0, 1000000)}.png"
170
- input_path = os.path.join(input_dir, temp_filename)
171
- input_image.save(input_path)
172
-
173
- # Nodes
174
- load_image_node = NODE_CLASS_MAPPINGS["LoadImage"]()
175
- image_loaded = load_image_node.load_image(image=temp_filename)
176
- image = get_value_at_index(image_loaded, 0)
177
-
178
- text_multiline = NODE_CLASS_MAPPINGS["Text Multiline"]()
179
- text_out = text_multiline.text_multiline(text=custom_prompt if custom_prompt.strip() else "")
180
- prompt_text = get_value_at_index(text_out, 0)
181
 
 
 
 
 
 
182
  dualcliploader = NODE_CLASS_MAPPINGS["DualCLIPLoader"]()
183
- clip_out = dualcliploader.load_clip(
184
  clip_name1="clip_l.safetensors",
185
  clip_name2="t5xxl_fp8_e4m3fn.safetensors",
186
  type="flux",
187
  )
188
- clip = get_value_at_index(clip_out, 0)
189
 
190
  cliptextencode = NODE_CLASS_MAPPINGS["CLIPTextEncode"]()
191
- conditioning = get_value_at_index(cliptextencode.encode(text=prompt_text, clip=clip), 0)
192
-
193
- fluxguidance = NODE_CLASS_MAPPINGS["FluxGuidance"]()
194
- positive_out = fluxguidance.append(guidance=3.5, conditioning=conditioning) # Using 3.5 as in original app
195
- positive = get_value_at_index(positive_out, 0)
196
-
197
- conditioningzeroout = NODE_CLASS_MAPPINGS["ConditioningZeroOut"]()
198
- negative_out = conditioningzeroout.zero_out(conditioning=conditioning)
199
- negative = get_value_at_index(negative_out, 0)
200
 
201
- upscale_name = "RealESRGAN_x2.pth" if upscale_factor == 2 else "RealESRGAN_x4.pth"
202
  upscalemodelloader = NODE_CLASS_MAPPINGS["UpscaleModelLoader"]()
203
- upscale_model = get_value_at_index(upscalemodelloader.load_model(model_name=upscale_name), 0)
 
 
204
 
205
  vaeloader = NODE_CLASS_MAPPINGS["VAELoader"]()
206
- vae = get_value_at_index(vaeloader.load_vae(vae_name="ae.safetensors"), 0)
 
 
 
 
 
207
 
208
- unetloader = NODE_CLASS_MAPPINGS["LoadDiffusionModel"]()
209
- model = get_value_at_index(unetloader.load_diffusion_model(unet_name="flux1-dev-fp8.safetensors", weight_dtype="fp8_e4m3fn"), 0)
 
 
 
 
 
210
 
211
  ultimatesdupscale = NODE_CLASS_MAPPINGS["UltimateSDUpscale"]()
212
- upscale_out = ultimatesdupscale.upscale(
213
- upscale_by=float(upscale_factor),
214
  seed=seed,
215
  steps=num_inference_steps,
216
- cfg=1.0,
217
  sampler_name="euler",
218
  scheduler="normal",
219
  denoise=denoising_strength,
@@ -223,47 +249,45 @@ def enhance_image(
223
  mask_blur=8,
224
  tile_padding=32,
225
  seam_fix_mode="None",
226
- seam_fix_denoise=1.0,
227
  seam_fix_width=64,
228
  seam_fix_mask_blur=8,
229
  seam_fix_padding=16,
230
  force_uniform_tiles=True,
231
  tiled_decode=False,
232
- image=image,
233
- model=model,
234
- positive=positive,
235
- negative=negative,
236
- vae=vae,
237
- upscale_model=upscale_model,
238
  )
239
- upscaled_tensor = get_value_at_index(upscale_out, 0)
240
 
241
- # Convert to PIL
242
- upscaled_img = Image.fromarray((upscaled_tensor[0].cpu().numpy() * 255).astype(np.uint8))
243
 
244
- target_w, target_h = w_original * upscale_factor, h_original * upscale_factor
245
- if upscaled_img.size != (target_w, target_h):
246
- upscaled_img = upscaled_img.resize((target_w, target_h), resample=Image.LANCZOS)
247
 
248
- if was_resized:
249
- upscaled_img = upscaled_img.resize((target_w, target_h), resample=Image.LANCZOS)
 
250
 
251
- resized_input = true_input_image.resize(upscaled_img.size, resample=Image.LANCZOS)
 
 
252
 
253
- # Cleanup temp file
254
- os.remove(input_path)
255
 
256
- return [resized_input, upscaled_img]
257
 
258
- # Gradio interface similar to original
259
- with gr.Blocks(css=css, title="🎨 AI Image Upscaler - Flux FP8") as demo:
260
  gr.HTML("""
261
  <div class="main-header">
262
- <h1>🎨 AI Image Upscaler - Flux FP8</h1>
263
- <p>Upscale images using Flux FP8 with ComfyUI workflow</p>
264
- <p>Running on <strong>{}</strong></p>
265
  </div>
266
- """.format(power_device))
267
 
268
  with gr.Row():
269
  with gr.Column(scale=1):
@@ -271,7 +295,11 @@ with gr.Blocks(css=css, title="🎨 AI Image Upscaler - Flux FP8") as demo:
271
 
272
  with gr.Tabs():
273
  with gr.TabItem("📁 Upload Image"):
274
- input_image = gr.Image(label="Upload Image", type="pil", height=200)
 
 
 
 
275
 
276
  with gr.TabItem("🔗 Image URL"):
277
  image_url = gr.Textbox(
@@ -295,15 +323,17 @@ with gr.Blocks(css=css, title="🎨 AI Image Upscaler - Flux FP8") as demo:
295
  minimum=1,
296
  maximum=4,
297
  step=1,
298
- value=2
 
299
  )
300
 
301
  num_inference_steps = gr.Slider(
302
- label="Inference Steps",
303
  minimum=1,
304
  maximum=50,
305
  step=1,
306
- value=25
 
307
  )
308
 
309
  denoising_strength = gr.Slider(
@@ -311,7 +341,8 @@ with gr.Blocks(css=css, title="🎨 AI Image Upscaler - Flux FP8") as demo:
311
  minimum=0.0,
312
  maximum=1.0,
313
  step=0.05,
314
- value=0.3
 
315
  )
316
 
317
  tile_size = gr.Slider(
@@ -319,19 +350,40 @@ with gr.Blocks(css=css, title="🎨 AI Image Upscaler - Flux FP8") as demo:
319
  minimum=256,
320
  maximum=2048,
321
  step=64,
322
- value=1024
 
323
  )
324
 
325
  with gr.Row():
326
- randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
327
- seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42)
 
 
 
 
 
 
 
 
 
 
328
 
329
- enhance_btn = gr.Button("🚀 Upscale Image", variant="primary", size="lg")
 
 
 
 
330
 
331
  with gr.Column(scale=2):
332
  gr.HTML("<h3>📊 Results</h3>")
333
 
334
- result_slider = ImageSlider(type="pil", interactive=False, height=600, label=None)
 
 
 
 
 
 
335
 
336
  enhance_btn.click(
337
  fn=enhance_image,
@@ -351,18 +403,63 @@ with gr.Blocks(css=css, title="🎨 AI Image Upscaler - Flux FP8") as demo:
351
 
352
  gr.HTML("""
353
  <div style="margin-top: 2rem; padding: 1rem; background: #f0f0f0; border-radius: 8px;">
354
- <p><strong>Note:</strong> Uses Flux FP8 model. Ensure compliance with licenses for commercial use.</p>
355
  </div>
356
  """)
357
 
358
  gr.HTML("""
359
  <style>
360
- #result_slider .slider { width: 100% !important; }
361
- #result_slider img { object-fit: contain !important; width: 100% !important; height: auto !important; }
362
- #result_slider .gr-button-tool, #result_slider .gr-button-undo, #result_slider .gr-button-clear { display: none !important; }
363
- #result_slider .badge-container .badge { display: none !important; }
364
- #result_slider .badge-container::before { content: "Before"; position: absolute; top: 10px; left: 10px; background: rgba(0,0,0,0.5); color: white; padding: 5px; border-radius: 5px; z-index: 10; }
365
- #result_slider .badge-container::after { content: "After"; position: absolute; top: 10px; right: 10px; background: rgba(0,0,0,0.5); color: white; padding: 5px; border-radius: 5px; z-index: 10; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  </style>
367
  """)
368
 
@@ -370,7 +467,10 @@ with gr.Blocks(css=css, title="🎨 AI Image Upscaler - Flux FP8") as demo:
370
  <script>
371
  document.addEventListener('DOMContentLoaded', function() {
372
  const sliderInput = document.querySelector('#result_slider input[type="range"]');
373
- if (sliderInput) { sliderInput.value = 50; sliderInput.dispatchEvent(new Event('input')); }
 
 
 
374
  });
375
  </script>
376
  """)
 
8
  import torch
9
  from gradio_imageslider import ImageSlider
10
  from PIL import Image
 
 
 
11
  from huggingface_hub import hf_hub_download
12
+ import subprocess
13
+ import sys
14
  import tempfile
15
+ from typing import Sequence, Mapping, Any, Union
16
+ import asyncio
17
+ import execution
18
+ from nodes import init_extra_nodes
19
+ import server
20
+
21
+ # Copy functions from FluxSimpleUpscaler.txt
22
+ def get_value_at_index(obj: Union[Sequence, Mapping], index: int) -> Any:
23
+ try:
24
+ return obj[index]
25
+ except KeyError:
26
+ return obj["result"][index]
27
 
28
+ def find_path(name: str, path: str = None) -> str:
29
+ if path is None:
30
+ path = os.getcwd()
31
+ if name in os.listdir(path):
32
+ path_name = os.path.join(path, name)
33
+ print(f"{name} found: {path_name}")
34
+ return path_name
35
+ parent_directory = os.path.dirname(path)
36
+ if parent_directory == path:
37
+ return None
38
+ return find_path(name, parent_directory)
39
+
40
+ def add_comfyui_directory_to_sys_path() -> None:
41
+ comfyui_path = find_path("ComfyUI")
42
+ if comfyui_path is not None and os.path.isdir(comfyui_path):
43
+ sys.path.append(comfyui_path)
44
+ print(f"'{comfyui_path}' added to sys.path")
45
+
46
+ def add_extra_model_paths() -> None:
47
+ try:
48
+ from main import load_extra_path_config
49
+ except ImportError:
50
+ print("Could not import load_extra_path_config from main.py. Looking in utils.extra_config instead.")
51
+ from utils.extra_config import load_extra_path_config
52
+ extra_model_paths = find_path("extra_model_paths.yaml")
53
+ if extra_model_paths is not None:
54
+ load_extra_path_config(extra_model_paths)
55
+ else:
56
+ print("Could not find the extra_model_paths config file.")
57
+
58
+ def import_custom_nodes() -> None:
59
+ import asyncio
60
+ import execution
61
+ from nodes import init_extra_nodes
62
+ import server
63
+ loop = asyncio.new_event_loop()
64
+ asyncio.set_event_loop(loop)
65
+ server_instance = server.PromptServer(loop)
66
+ execution.PromptQueue(server_instance)
67
+ init_extra_nodes()
68
 
69
  # Setup ComfyUI and custom nodes
70
  if not os.path.exists("ComfyUI"):
71
+ subprocess.run(["git", "clone", "https://github.com/comfyanonymous/ComfyUI.git"])
72
+
73
+ custom_node_path = "ComfyUI/custom_nodes/ComfyUI_UltimateSDUpscale"
74
+ if not os.path.exists(custom_node_path):
75
+ subprocess.run(["git", "clone", "https://github.com/ssitu/ComfyUI_UltimateSDUpscale.git", custom_node_path])
76
+
77
+ # Create model directories
78
+ os.makedirs("ComfyUI/models/unet", exist_ok=True)
79
+ os.makedirs("ComfyUI/models/clip", exist_ok=True)
80
+ os.makedirs("ComfyUI/models/vae", exist_ok=True)
81
+ os.makedirs("ComfyUI/models/upscale_models", exist_ok=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
  # Download models if not present
84
+ unet_path = "ComfyUI/models/unet/flux1-dev-fp8.safetensors"
85
+ if not os.path.exists(unet_path):
86
+ hf_hub_download("Kijai/flux-fp8", "flux1-dev-fp8.safetensors", local_dir="ComfyUI/models/unet")
87
+
88
+ clip_l_path = "ComfyUI/models/clip/clip_l.safetensors"
89
+ if not os.path.exists(clip_l_path):
90
+ hf_hub_download("comfyanonymous/flux_text_encoders", "clip_l.safetensors", local_dir="ComfyUI/models/clip")
91
+
92
+ t5_path = "ComfyUI/models/clip/t5xxl_fp8_e4m3fn.safetensors"
93
+ if not os.path.exists(t5_path):
94
+ hf_hub_download("comfyanonymous/flux_text_encoders", "t5xxl_fp8_e4m3fn.safetensors", local_dir="ComfyUI/models/clip")
95
+
96
+ vae_path = "ComfyUI/models/vae/ae.safetensors"
97
+ if not os.path.exists(vae_path):
98
+ hf_hub_download("black-forest-labs/FLUX.1-dev", "ae.safetensors", subfolder="vae", local_dir="ComfyUI/models/vae")
99
+
100
+ esrgan_x2_path = "ComfyUI/models/upscale_models/RealESRGAN_x2.pth"
101
+ if not os.path.exists(esrgan_x2_path):
102
+ hf_hub_download("ai-forever/Real-ESRGAN", "RealESRGAN_x2.pth", local_dir="ComfyUI/models/upscale_models")
103
+
104
+ esrgan_x4_path = "ComfyUI/models/upscale_models/RealESRGAN_x4.pth"
105
+ if not os.path.exists(esrgan_x4_path):
106
+ hf_hub_download("ai-forever/Real-ESRGAN", "RealESRGAN_x4.pth", local_dir="ComfyUI/models/upscale_models")
107
+
108
+ # Add ComfyUI to path and import custom nodes
109
+ add_comfyui_directory_to_sys_path()
110
+ add_extra_model_paths()
111
+ import_custom_nodes()
112
+
113
+ from nodes import NODE_CLASS_MAPPINGS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
 
115
  css = """
116
  #col-container {
117
  margin: 0 auto;
 
123
  }
124
  """
125
 
 
126
  MAX_SEED = 1000000
127
  MAX_PIXEL_BUDGET = 8192 * 8192
128
 
 
136
  was_resized = False
137
 
138
  if w * h * upscale_factor**2 > MAX_PIXEL_BUDGET:
139
+ gr.Info(f"Requested output image is too large. Resizing input to fit within pixel budget.")
140
  target_input_pixels = MAX_PIXEL_BUDGET / (upscale_factor ** 2)
141
  scale = (target_input_pixels / (w * h)) ** 0.5
142
  new_w = max(16, int(w * scale) // 16 * 16)
 
146
 
147
  return input_image, w_original, h_original, was_resized
148
 
149
+ import requests
150
  def load_image_from_url(url):
151
  try:
152
  response = requests.get(url, stream=True)
153
  response.raise_for_status()
154
  return Image.open(response.raw)
155
  except Exception as e:
156
+ raise gr.Error(f"Failed to load image from URL: {e}")
157
+
158
+ def tensor_to_pil(tensor):
159
+ tensor = tensor.cpu().clamp(0, 1) * 255
160
+ img = tensor.numpy().astype(np.uint8)[0]
161
+ return Image.fromarray(img)
162
 
163
  @spaces.GPU(duration=120)
164
  def enhance_image(
 
173
  tile_size,
174
  progress=gr.Progress(track_tqdm=True),
175
  ):
176
+ if image_input is not None:
177
+ true_input_image = image_input
178
+ elif image_url:
179
+ true_input_image = load_image_from_url(image_url)
180
+ else:
181
+ raise gr.Error("Please provide an image (upload or URL)")
182
+
183
+ if randomize_seed:
184
+ seed = random.randint(0, MAX_SEED)
185
+
186
+ input_image, w_original, h_original, was_resized = process_input(true_input_image, upscale_factor)
187
+
188
+ if upscale_factor == 2:
189
+ upscale_model_name = "RealESRGAN_x2.pth"
190
+ else:
191
+ upscale_model_name = "RealESRGAN_x4.pth"
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
194
+ input_image.save(tmp.name)
195
+ image_path = tmp.name
196
+
197
+ with torch.inference_mode():
198
  dualcliploader = NODE_CLASS_MAPPINGS["DualCLIPLoader"]()
199
+ dualcliploader_res = dualcliploader.load_clip(
200
  clip_name1="clip_l.safetensors",
201
  clip_name2="t5xxl_fp8_e4m3fn.safetensors",
202
  type="flux",
203
  )
204
+ clip = get_value_at_index(dualcliploader_res, 0)
205
 
206
  cliptextencode = NODE_CLASS_MAPPINGS["CLIPTextEncode"]()
207
+ positive_res = cliptextencode.encode(
208
+ text=custom_prompt,
209
+ clip=clip
210
+ )
211
+ negative_res = cliptextencode.encode(
212
+ text="",
213
+ clip=clip
214
+ )
 
215
 
 
216
  upscalemodelloader = NODE_CLASS_MAPPINGS["UpscaleModelLoader"]()
217
+ upscalemodelloader_res = upscalemodelloader.load_model(
218
+ model_name=upscale_model_name
219
+ )
220
 
221
  vaeloader = NODE_CLASS_MAPPINGS["VAELoader"]()
222
+ vaeloader_res = vaeloader.load_vae(vae_name="ae.safetensors")
223
+
224
+ unetloader = NODE_CLASS_MAPPINGS["UNETLoader"]()
225
+ unetloader_res = unetloader.load_unet(
226
+ unet_name="flux1-dev-fp8.safetensors", weight_dtype="fp8_e4m3fn"
227
+ )
228
 
229
+ loadimage = NODE_CLASS_MAPPINGS["LoadImage"]()
230
+ loadimage_res = loadimage.load_image(image=os.path.basename(image_path))
231
+
232
+ fluxguidance = NODE_CLASS_MAPPINGS["FluxGuidance"]()
233
+ fluxguidance_res = fluxguidance.append(
234
+ guidance=30, conditioning=get_value_at_index(positive_res, 0)
235
+ )
236
 
237
  ultimatesdupscale = NODE_CLASS_MAPPINGS["UltimateSDUpscale"]()
238
+ usd_res = ultimatesdupscale.upscale(
239
+ upscale_by=upscale_factor,
240
  seed=seed,
241
  steps=num_inference_steps,
242
+ cfg=1,
243
  sampler_name="euler",
244
  scheduler="normal",
245
  denoise=denoising_strength,
 
249
  mask_blur=8,
250
  tile_padding=32,
251
  seam_fix_mode="None",
252
+ seam_fix_denoise=1,
253
  seam_fix_width=64,
254
  seam_fix_mask_blur=8,
255
  seam_fix_padding=16,
256
  force_uniform_tiles=True,
257
  tiled_decode=False,
258
+ image=get_value_at_index(loadimage_res, 0),
259
+ model=get_value_at_index(unetloader_res, 0),
260
+ positive=get_value_at_index(fluxguidance_res, 0),
261
+ negative=get_value_at_index(negative_res, 0),
262
+ vae=get_value_at_index(vaeloader_res, 0),
263
+ upscale_model=get_value_at_index(upscalemodelloader_res, 0),
264
  )
 
265
 
266
+ output_tensor = get_value_at_index(usd_res, 0)
267
+ image = tensor_to_pil(output_tensor)
268
 
269
+ os.unlink(image_path)
 
 
270
 
271
+ target_w, target_h = w_original * upscale_factor, h_original * upscale_factor
272
+ if image.size != (target_w, target_h):
273
+ image = image.resize((target_w, target_h), resample=Image.LANCZOS)
274
 
275
+ if was_resized:
276
+ gr.Info(f"Resizing output to target size: {target_w}x{target_h}")
277
+ image = image.resize((target_w, target_h), resample=Image.LANCZOS)
278
 
279
+ resized_input = true_input_image.resize(image.size, resample=Image.LANCZOS)
 
280
 
281
+ return [resized_input, image]
282
 
283
+ with gr.Blocks(css=css, title="🎨 AI Image Upscaler - FLUX ComfyUI") as demo:
 
284
  gr.HTML("""
285
  <div class="main-header">
286
+ <h1>🎨 AI Image Upscaler (ComfyUI Workflow)</h1>
287
+ <p>Upload an image or provide a URL to upscale it using FLUX FP8 with ComfyUI Ultimate SD Upscale</p>
288
+ <p>Using FLUX.1-dev FP8 model</p>
289
  </div>
290
+ """)
291
 
292
  with gr.Row():
293
  with gr.Column(scale=1):
 
295
 
296
  with gr.Tabs():
297
  with gr.TabItem("📁 Upload Image"):
298
+ input_image = gr.Image(
299
+ label="Upload Image",
300
+ type="pil",
301
+ height=200
302
+ )
303
 
304
  with gr.TabItem("🔗 Image URL"):
305
  image_url = gr.Textbox(
 
323
  minimum=1,
324
  maximum=4,
325
  step=1,
326
+ value=2,
327
+ info="How much to upscale the image"
328
  )
329
 
330
  num_inference_steps = gr.Slider(
331
+ label="Number of Inference Steps",
332
  minimum=1,
333
  maximum=50,
334
  step=1,
335
+ value=25,
336
+ info="More steps = better quality but slower"
337
  )
338
 
339
  denoising_strength = gr.Slider(
 
341
  minimum=0.0,
342
  maximum=1.0,
343
  step=0.05,
344
+ value=0.3,
345
+ info="Controls how much the image is transformed"
346
  )
347
 
348
  tile_size = gr.Slider(
 
350
  minimum=256,
351
  maximum=2048,
352
  step=64,
353
+ value=1024,
354
+ info="Size of tiles for processing (larger = faster but more memory)"
355
  )
356
 
357
  with gr.Row():
358
+ randomize_seed = gr.Checkbox(
359
+ label="Randomize seed",
360
+ value=True
361
+ )
362
+ seed = gr.Slider(
363
+ label="Seed",
364
+ minimum=0,
365
+ maximum=MAX_SEED,
366
+ step=1,
367
+ value=42,
368
+ interactive=True
369
+ )
370
 
371
+ enhance_btn = gr.Button(
372
+ "🚀 Upscale Image",
373
+ variant="primary",
374
+ size="lg"
375
+ )
376
 
377
  with gr.Column(scale=2):
378
  gr.HTML("<h3>📊 Results</h3>")
379
 
380
+ result_slider = ImageSlider(
381
+ type="pil",
382
+ interactive=False,
383
+ height=600,
384
+ elem_id="result_slider",
385
+ label=None
386
+ )
387
 
388
  enhance_btn.click(
389
  fn=enhance_image,
 
403
 
404
  gr.HTML("""
405
  <div style="margin-top: 2rem; padding: 1rem; background: #f0f0f0; border-radius: 8px;">
406
+ <p><strong>Note:</strong> This upscaler uses the Flux.1-dev model. Users are responsible for obtaining commercial rights if used commercially under their license.</p>
407
  </div>
408
  """)
409
 
410
  gr.HTML("""
411
  <style>
412
+ #result_slider .slider {
413
+ width: 100% !important;
414
+ max-width: inherit !important;
415
+ }
416
+ #result_slider img {
417
+ object-fit: contain !important;
418
+ width: 100% !important;
419
+ height: auto !important;
420
+ }
421
+ #result_slider .gr-button-tool {
422
+ display: none !important;
423
+ }
424
+ #result_slider .gr-button-undo {
425
+ display: none !important;
426
+ }
427
+ #result_slider .gr-button-clear {
428
+ display: none !important;
429
+ }
430
+ #result_slider .badge-container .badge {
431
+ display: none !important;
432
+ }
433
+ #result_slider .badge-container::before {
434
+ content: "Before";
435
+ position: absolute;
436
+ top: 10px;
437
+ left: 10px;
438
+ background: rgba(0,0,0,0.5);
439
+ color: white;
440
+ padding: 5px;
441
+ border-radius: 5px;
442
+ z-index: 10;
443
+ }
444
+ #result_slider .badge-container::after {
445
+ content: "After";
446
+ position: absolute;
447
+ top: 10px;
448
+ right: 10px;
449
+ background: rgba(0,0,0,0.5);
450
+ color: white;
451
+ padding: 5px;
452
+ border-radius: 5px;
453
+ z-index: 10;
454
+ }
455
+ #result_slider .fullscreen img {
456
+ object-fit: contain !important;
457
+ width: 100vw !important;
458
+ height: 100vh !important;
459
+ position: absolute;
460
+ top: 0;
461
+ left: 0;
462
+ }
463
  </style>
464
  """)
465
 
 
467
  <script>
468
  document.addEventListener('DOMContentLoaded', function() {
469
  const sliderInput = document.querySelector('#result_slider input[type="range"]');
470
+ if (sliderInput) {
471
+ sliderInput.value = 50;
472
+ sliderInput.dispatchEvent(new Event('input'));
473
+ }
474
  });
475
  </script>
476
  """)