Philippe Potvin commited on
Commit
f1d60fc
·
1 Parent(s): 5d5959d

Thu, 02 Jul 2026 19:47 - Optimize Qwen enhancement stage

Browse files
Files changed (3) hide show
  1. README.md +2 -2
  2. app.py +196 -157
  3. requirements.txt +12 -16
README.md CHANGED
@@ -7,10 +7,10 @@ sdk_version: 5.49.1
7
  app_file: app.py
8
  pinned: false
9
  license: apache-2.0
10
- version: 1.0.0
11
  short_description: Powerful image editing - supports one or two input images.
12
  ---
13
 
14
- Pro Realism Edit Studio is a powerful image editor powered by [Qwen-Image-Edit-2511](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) with [Phr00t's Rapid-AIO v23](https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO) accelerated transformer for 4-step inference. Upload one or two input images, write a prompt, get high-quality results. Enhanced with Real-ESRGAN upscaling, GFPGAN face restoration, and multi-stage detail enhancement.
15
 
16
  Video generation is disabled unless an owned Gradio video Space is configured with `VIDEO_SPACE_ID`.
 
7
  app_file: app.py
8
  pinned: false
9
  license: apache-2.0
10
+ version: 1.0.1
11
  short_description: Powerful image editing - supports one or two input images.
12
  ---
13
 
14
+ Pro Realism Edit Studio is a powerful image editor powered by [Qwen-Image-Edit-2511](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) with [Phr00t's Rapid-AIO v23](https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO) accelerated transformer for 4-step inference. Upload one or two input images, write a prompt, get high-quality results. The enhancement stage uses Nomos-family tiled upscaling, Qwen-aware masked artifact repair, micro-detail sharpening, and final photographic grain.
15
 
16
  Video generation is disabled unless an owned Gradio video Space is configured with `VIDEO_SPACE_ID`.
app.py CHANGED
@@ -5,12 +5,11 @@ Pro Realism Edit Studio - Enhanced Edition
5
 
6
  Advanced image editing and enhancement studio powered by:
7
  - Qwen-Image-Edit-2511 with Phr00t's Rapid-AIO v23 accelerated transformer
8
- - Real-ESRGAN for high-quality upscaling
9
- - GFPGAN/CodeFormer for face restoration
10
- - Multi-stage detail enhancement pipeline
11
 
12
  Author: Enhanced with Hugging Face CLI and image generation expertise
13
- Version: 1.0.0
14
  """
15
 
16
  import gradio as gr
@@ -43,7 +42,7 @@ from gradio_client import Client, handle_file
43
 
44
  # Base model configuration
45
  BASE_MODEL_ID = "Qwen/Qwen-Image-Edit-2511"
46
- APP_VERSION = "1.0.0"
47
  PHR00T_REPO_ID = os.environ.get("PHR00T_REPO_ID", "Phr00t/Qwen-Image-Edit-Rapid-AIO").strip()
48
  RAPID_TRANSFORMER_FILENAME = os.environ.get(
49
  "RAPID_TRANSFORMER_FILENAME",
@@ -52,17 +51,15 @@ RAPID_TRANSFORMER_FILENAME = os.environ.get(
52
  PHR00T_TRANSFORMER_PREFIX = "model.diffusion_model."
53
  VIDEO_SPACE_ID = os.environ.get("VIDEO_SPACE_ID", "").strip()
54
 
55
- # Enhanced Upscaler Configuration
56
- UPSCALER_MODEL_ID = os.environ.get("UPSCALER_MODEL_ID", "ai-forever/Real-ESRGAN").strip()
57
- UPSCALER_MODEL_FILENAME = os.environ.get("UPSCALER_MODEL_FILENAME", "RealESRGAN_x4plus.pth").strip()
58
  UPSCALER_TILE_SIZE = int(os.environ.get("UPSCALER_TILE_SIZE", "512"))
59
  UPSCALER_TILE_OVERLAP = int(os.environ.get("UPSCALER_TILE_OVERLAP", "64"))
60
  ENHANCE_MAX_INPUT_EDGE = int(os.environ.get("ENHANCE_MAX_INPUT_EDGE", "2048"))
61
- ENHANCE_GRAIN_STRENGTH = float(os.environ.get("ENHANCE_GRAIN_STRENGTH", "0.015"))
62
-
63
- # Face Restoration Configuration
64
- FACE_RESTORATION_MODEL = os.environ.get("FACE_RESTORATION_MODEL", "Xintao/GFPGAN").strip()
65
- FACE_RESTORATION_WEIGHTS = os.environ.get("FACE_RESTORATION_WEIGHTS", "GFPGANv1.3.pth").strip()
66
 
67
  # Advanced Detail Enhancement Configuration
68
  DETAIL_ENHANCEMENT_ENABLED = os.environ.get("DETAIL_ENHANCEMENT_ENABLED", "true").lower() == "true"
@@ -73,26 +70,16 @@ SMART_SHARPENING_STRENGTH = float(os.environ.get("SMART_SHARPENING_STRENGTH", "1
73
  # ============================================================================
74
 
75
  ENHANCE_MODE_OFF = "Off"
76
- ENHANCE_MODE_UPSCALE = "Upscale Only"
77
- ENHANCE_MODE_CLEAN = "Clean & Restore"
78
  ENHANCE_MODE_MAX_DETAIL = "Max Detail"
79
- ENHANCE_MODE_FACE_ENHANCE = "Face Enhance"
80
- ENHANCE_MODE_FULL_ENHANCE = "Full Enhance"
81
- ENHANCE_MODE_CHOICES = [
82
- ENHANCE_MODE_OFF,
83
- ENHANCE_MODE_UPSCALE,
84
- ENHANCE_MODE_CLEAN,
85
- ENHANCE_MODE_MAX_DETAIL,
86
- ENHANCE_MODE_FACE_ENHANCE,
87
- ENHANCE_MODE_FULL_ENHANCE
88
- ]
89
 
90
  # ============================================================================
91
  # GLOBAL MODEL CACHE
92
  # ============================================================================
93
 
94
  _upscaler_model = None
95
- _face_restoration_model = None
96
  _detail_enhancement_model = None
97
 
98
  # ============================================================================
@@ -261,11 +248,11 @@ pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
261
  print("Applied FA3 attention processor optimization")
262
 
263
  # ============================================================================
264
- # ENHANCED UPSCALER (Real-ESRGAN based)
265
  # ============================================================================
266
 
267
  def load_upscaler_model():
268
- """Load Real-ESRGAN model for high-quality upscaling"""
269
  global _upscaler_model
270
  if _upscaler_model is not None:
271
  return _upscaler_model
@@ -278,24 +265,29 @@ def load_upscaler_model():
278
  raise gr.Error("Enhance mode requires spandrel and spandrel_extra_arches to be installed. "
279
  "Install with: pip install spandrel spandrel_extra_arches") from exc
280
 
281
- try:
282
- model_path = download_model_with_retry(UPSCALER_MODEL_ID, UPSCALER_MODEL_FILENAME)
283
- model = spandrel.ModelLoader().load_from_file(model_path)
284
- model.eval().to(device)
285
- _upscaler_model = model
286
- print(f"Successfully loaded upscaler: {UPSCALER_MODEL_ID}/{UPSCALER_MODEL_FILENAME}")
287
- return _upscaler_model
288
- except Exception as e:
289
- print(f"Failed to load upscaler model: {e}")
290
- print("Falling back to Nomos upscaler...")
 
291
  try:
292
- model_path = download_model_with_retry("Phips/4xNomos8k_atd_jpg", "4xNomos8k_atd_jpg.safetensors")
293
  model = spandrel.ModelLoader().load_from_file(model_path)
294
  model.eval().to(device)
295
  _upscaler_model = model
 
296
  return _upscaler_model
297
- except Exception as fallback_error:
298
- raise gr.Error(f"Failed to load all upscaler models: {e} | {fallback_error}")
 
 
 
299
 
300
  def image_to_tensor(image):
301
  """Convert PIL Image to tensor"""
@@ -318,11 +310,24 @@ def validate_enhance_input_size(image):
318
  f"Consider resizing your image first."
319
  )
320
 
321
- def advanced_tile_upscale(image, scale=4):
322
- """
323
- Advanced tiling upscaler with improved blending and edge handling
324
- Uses Real-ESRGAN for superior quality compared to Nomos
325
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  validate_enhance_input_size(image)
327
  model = load_upscaler_model()
328
  tensor = image_to_tensor(image)
@@ -355,7 +360,10 @@ def advanced_tile_upscale(image, scale=4):
355
  x1 = min(x + tile_size, width)
356
  tile = tensor[:, :, y:y1, x:x1]
357
 
358
- upscaled_tile = model(tile).clamp(0, 1)
 
 
 
359
 
360
  scale_y = upscaled_tile.shape[-2] // tile.shape[-2]
361
  scale_x = upscaled_tile.shape[-1] // tile.shape[-1]
@@ -370,12 +378,27 @@ def advanced_tile_upscale(image, scale=4):
370
 
371
  oy0, oy1 = y * scale_y, y1 * scale_y
372
  ox0, ox1 = x * scale_x, x1 * scale_x
373
- output[:, :, oy0:oy1, ox0:ox1] += upscaled_tile
374
- weights[:, :, oy0:oy1, ox0:ox1] += 1
 
 
 
 
 
 
 
 
 
 
 
 
375
 
376
  output = output / weights.clamp_min(1)
377
  return tensor_to_image(output)
378
 
 
 
 
379
  # ============================================================================
380
  # ENHANCED DETAILER (Multi-stage processing)
381
  # ============================================================================
@@ -446,54 +469,9 @@ def apply_high_frequency_details(image, amount=0.6):
446
  return result
447
 
448
  # ============================================================================
449
- # ENHANCED CLEANER (Face Restoration + Artifact Removal)
450
  # ============================================================================
451
 
452
- def load_face_restoration_model():
453
- """Load GFPGAN model for face restoration"""
454
- global _face_restoration_model
455
- if _face_restoration_model is not None:
456
- return _face_restoration_model
457
-
458
- try:
459
- from gfpgan import GFPGANer
460
- model_path = download_model_with_retry(FACE_RESTORATION_MODEL, FACE_RESTORATION_WEIGHTS)
461
-
462
- restorer = GFPGANer(
463
- model_path=model_path,
464
- upscale=1,
465
- arch='clean',
466
- channel_multiplier=2,
467
- bg_upsampler=None
468
- )
469
-
470
- _face_restoration_model = restorer
471
- print("Successfully loaded GFPGAN face restoration model")
472
- return _face_restoration_model
473
-
474
- except ImportError:
475
- print("GFPGAN not available, face restoration will use fallback methods")
476
- return None
477
- except Exception as e:
478
- print(f"Failed to load face restoration model: {e}")
479
- return None
480
-
481
- def restore_faces(image):
482
- """Restore faces in an image using GFPGAN"""
483
- restorer = load_face_restoration_model()
484
- if restorer is None:
485
- print("Face restoration model not available, using skin repair fallback")
486
- return repair_skin_texture(image)
487
-
488
- try:
489
- img_array = np.array(image.convert("RGB"))
490
- restored_array, _ = restorer.enhance(img_array, has_aligned=False, only_center_face=False, paste_back=True)
491
- restored_image = Image.fromarray(restored_array.astype(np.uint8))
492
- return restored_image
493
- except Exception as e:
494
- print(f"Face restoration failed: {e}, using skin repair fallback")
495
- return repair_skin_texture(image)
496
-
497
  def remove_artifacts(image):
498
  """Remove compression artifacts and noise"""
499
  denoised = image.filter(ImageFilter.MedianFilter(size=3))
@@ -563,14 +541,96 @@ def add_film_grain(image, seed):
563
  array = np.clip(array + grain, 0, 255)
564
  return Image.fromarray(array.astype(np.uint8), mode="RGB")
565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
  # ============================================================================
567
  # ENHANCED APPLY ENHANCEMENT (Main enhancement pipeline)
568
  # ============================================================================
569
 
570
  def apply_enhancement(image, enhance_mode, seed=0, progress=None):
571
- """
572
- Apply various enhancement modes to the image
573
- """
574
  mode = enhance_mode or ENHANCE_MODE_OFF
575
  if mode not in ENHANCE_MODE_CHOICES:
576
  raise gr.Error(f"Unknown enhance mode: {mode}")
@@ -578,61 +638,43 @@ def apply_enhancement(image, enhance_mode, seed=0, progress=None):
578
  return image
579
 
580
  enhanced = image.convert("RGB")
581
-
582
- total_steps = 0
583
- if mode == ENHANCE_MODE_UPSCALE:
584
- total_steps = 1
585
- elif mode == ENHANCE_MODE_CLEAN:
586
- total_steps = 3
587
- elif mode == ENHANCE_MODE_MAX_DETAIL:
588
- total_steps = 4
589
- elif mode == ENHANCE_MODE_FACE_ENHANCE:
590
- total_steps = 2
591
- elif mode == ENHANCE_MODE_FULL_ENHANCE:
592
- total_steps = 5
593
-
594
  step = 0
595
-
596
- if mode == ENHANCE_MODE_FACE_ENHANCE:
597
- if progress:
598
- step += 1
599
- progress(0.5 * step / total_steps, desc="Restoring faces...")
600
- enhanced = restore_faces(enhanced)
601
- if progress:
602
- step += 1
603
- progress(0.5 * step / total_steps, desc="Upscaling...")
604
- enhanced = advanced_tile_upscale(enhanced)
605
- return enhanced
606
-
607
- if mode in (ENHANCE_MODE_CLEAN, ENHANCE_MODE_FULL_ENHANCE):
608
- if progress:
609
- step += 1
610
- progress(0.7 * step / total_steps, desc="Removing artifacts...")
611
- enhanced = remove_artifacts(enhanced)
612
- if progress:
613
- step += 1
614
- progress(0.7 * step / total_steps, desc="Repairing skin and faces...")
615
- enhanced = enhanced_skin_repair(enhanced)
616
- enhanced = restore_faces(enhanced)
617
-
618
- if mode in (ENHANCE_MODE_UPSCALE, ENHANCE_MODE_CLEAN, ENHANCE_MODE_MAX_DETAIL, ENHANCE_MODE_FULL_ENHANCE):
619
  if progress:
620
  step += 1
621
- progress(0.8 * step / total_steps, desc="Upscaling image...")
622
- enhanced = advanced_tile_upscale(enhanced)
623
-
624
- if mode in (ENHANCE_MODE_MAX_DETAIL, ENHANCE_MODE_FULL_ENHANCE):
 
625
  if progress:
626
  step += 1
627
- progress(0.9 * step / total_steps, desc="Enhancing details...")
628
- enhanced = add_ultra_detail(enhanced, strength=0.7)
629
- enhanced = apply_high_frequency_details(enhanced, amount=0.5)
630
- enhanced = smart_sharpen(enhanced, strength=SMART_SHARPENING_STRENGTH)
 
 
 
 
 
631
  if progress:
632
  step += 1
633
- progress(0.95 * step / total_steps, desc="Adding final grain...")
634
- enhanced = add_film_grain(enhanced, seed)
635
-
 
636
  return enhanced
637
 
638
  # ============================================================================
@@ -796,9 +838,10 @@ css = """
796
  with gr.Blocks(css=css) as demo:
797
  with gr.Column(elem_id="col-container"):
798
  gr.HTML(f"""
 
799
  <div id="logo-title">
800
  <h1>Pro Realism Edit Studio - Enhanced</h1>
801
- <h2>Rapid Edit with Real-ESRGAN & Face Restoration</h2>
802
  </div>
803
  """)
804
 
@@ -806,15 +849,13 @@ with gr.Blocks(css=css) as demo:
806
  Powered by:
807
  - [Qwen-Image-Edit-2511](https://huggingface.co/Qwen/Qwen-Image-Edit-2511)
808
  - [Phr00t's Rapid-AIO v23](https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO) accelerated transformer
809
- - [Real-ESRGAN](https://huggingface.co/ai-forever/Real-ESRGAN) for high-quality upscaling
810
- - [GFPGAN](https://github.com/TencentARC/GFPGAN) for face restoration
811
 
812
  Upload an image and enter your prompt to edit it.
813
 
814
  Pro Tips:
815
- - Use Face Enhance mode for portrait photography
816
- - Use Max Detail for product shots and textures
817
- - Use Full Enhance for comprehensive improvement
818
  """)
819
 
820
  with gr.Row():
@@ -840,11 +881,9 @@ with gr.Blocks(css=css) as demo:
840
  enhance_info = gr.Markdown("""
841
  Enhancement Options:
842
  - Off: No post-processing
843
- - Upscale Only: 4x upscaling with Real-ESRGAN
844
- - Clean & Restore: Artifact removal + skin/face restoration
845
- - Max Detail: Full detail enhancement with sharpening
846
- - Face Enhance: Specialized face restoration + upscaling
847
- - Full Enhance: Complete pipeline (clean + detail + face + upscale)
848
  """, visible=False)
849
 
850
  run_button = gr.Button("Generate!", variant="primary")
 
5
 
6
  Advanced image editing and enhancement studio powered by:
7
  - Qwen-Image-Edit-2511 with Phr00t's Rapid-AIO v23 accelerated transformer
8
+ - Nomos-family tiled upscaling
9
+ - Qwen-aware masked cleanup and final photographic detail
 
10
 
11
  Author: Enhanced with Hugging Face CLI and image generation expertise
12
+ Version: 1.0.1
13
  """
14
 
15
  import gradio as gr
 
42
 
43
  # Base model configuration
44
  BASE_MODEL_ID = "Qwen/Qwen-Image-Edit-2511"
45
+ APP_VERSION = "1.0.1"
46
  PHR00T_REPO_ID = os.environ.get("PHR00T_REPO_ID", "Phr00t/Qwen-Image-Edit-Rapid-AIO").strip()
47
  RAPID_TRANSFORMER_FILENAME = os.environ.get(
48
  "RAPID_TRANSFORMER_FILENAME",
 
51
  PHR00T_TRANSFORMER_PREFIX = "model.diffusion_model."
52
  VIDEO_SPACE_ID = os.environ.get("VIDEO_SPACE_ID", "").strip()
53
 
54
+ # Qwen-optimized enhancement configuration
55
+ UPSCALER_MODEL_ID = os.environ.get("UPSCALER_MODEL_ID", "Phips/4xNomos8k_atd_jpg").strip()
56
+ UPSCALER_MODEL_FILENAME = os.environ.get("UPSCALER_MODEL_FILENAME", "4xNomos8k_atd_jpg.safetensors").strip()
57
  UPSCALER_TILE_SIZE = int(os.environ.get("UPSCALER_TILE_SIZE", "512"))
58
  UPSCALER_TILE_OVERLAP = int(os.environ.get("UPSCALER_TILE_OVERLAP", "64"))
59
  ENHANCE_MAX_INPUT_EDGE = int(os.environ.get("ENHANCE_MAX_INPUT_EDGE", "2048"))
60
+ ENHANCE_QWEN_DOWNSCALE_TRIGGER_EDGE = int(os.environ.get("ENHANCE_QWEN_DOWNSCALE_TRIGGER_EDGE", "1536"))
61
+ ENHANCE_QWEN_DOWNSCALE_FACTOR = float(os.environ.get("ENHANCE_QWEN_DOWNSCALE_FACTOR", "0.75"))
62
+ ENHANCE_GRAIN_STRENGTH = float(os.environ.get("ENHANCE_GRAIN_STRENGTH", "0.010"))
 
 
63
 
64
  # Advanced Detail Enhancement Configuration
65
  DETAIL_ENHANCEMENT_ENABLED = os.environ.get("DETAIL_ENHANCEMENT_ENABLED", "true").lower() == "true"
 
70
  # ============================================================================
71
 
72
  ENHANCE_MODE_OFF = "Off"
73
+ ENHANCE_MODE_UPSCALE = "Upscale"
74
+ ENHANCE_MODE_CLEAN = "Clean"
75
  ENHANCE_MODE_MAX_DETAIL = "Max Detail"
76
+ ENHANCE_MODE_CHOICES = [ENHANCE_MODE_OFF, ENHANCE_MODE_UPSCALE, ENHANCE_MODE_CLEAN, ENHANCE_MODE_MAX_DETAIL]
 
 
 
 
 
 
 
 
 
77
 
78
  # ============================================================================
79
  # GLOBAL MODEL CACHE
80
  # ============================================================================
81
 
82
  _upscaler_model = None
 
83
  _detail_enhancement_model = None
84
 
85
  # ============================================================================
 
248
  print("Applied FA3 attention processor optimization")
249
 
250
  # ============================================================================
251
+ # QWEN-OPTIMIZED UPSCALER
252
  # ============================================================================
253
 
254
  def load_upscaler_model():
255
+ """Load a Spandrel upscaler, defaulting to the Nomos model used by this Space."""
256
  global _upscaler_model
257
  if _upscaler_model is not None:
258
  return _upscaler_model
 
265
  raise gr.Error("Enhance mode requires spandrel and spandrel_extra_arches to be installed. "
266
  "Install with: pip install spandrel spandrel_extra_arches") from exc
267
 
268
+ candidates = [
269
+ (UPSCALER_MODEL_ID, UPSCALER_MODEL_FILENAME),
270
+ ("Phips/4xNomos8k_atd_jpg", "4xNomos8k_atd_jpg.safetensors"),
271
+ ]
272
+ seen = set()
273
+ errors = []
274
+ for repo_id, filename in candidates:
275
+ key = (repo_id, filename)
276
+ if key in seen:
277
+ continue
278
+ seen.add(key)
279
  try:
280
+ model_path = download_model_with_retry(repo_id, filename)
281
  model = spandrel.ModelLoader().load_from_file(model_path)
282
  model.eval().to(device)
283
  _upscaler_model = model
284
+ print(f"Successfully loaded upscaler: {repo_id}/{filename}")
285
  return _upscaler_model
286
+ except Exception as exc:
287
+ errors.append(f"{repo_id}/{filename}: {exc}")
288
+ print(f"Failed to load upscaler {repo_id}/{filename}: {exc}")
289
+
290
+ raise gr.Error(f"Failed to load all upscaler models: {' | '.join(errors)}")
291
 
292
  def image_to_tensor(image):
293
  """Convert PIL Image to tensor"""
 
310
  f"Consider resizing your image first."
311
  )
312
 
313
+ def _tile_weight(height, width, overlap_y, overlap_x, touches_top, touches_bottom, touches_left, touches_right, dtype, device):
314
+ weight = torch.ones((1, 1, height, width), dtype=dtype, device=device)
315
+ if overlap_y > 1 and not touches_top:
316
+ ramp = torch.linspace(0.0, 1.0, overlap_y, dtype=dtype, device=device).view(1, 1, overlap_y, 1)
317
+ weight[:, :, :overlap_y, :] *= ramp
318
+ if overlap_y > 1 and not touches_bottom:
319
+ ramp = torch.linspace(1.0, 0.0, overlap_y, dtype=dtype, device=device).view(1, 1, overlap_y, 1)
320
+ weight[:, :, -overlap_y:, :] *= ramp
321
+ if overlap_x > 1 and not touches_left:
322
+ ramp = torch.linspace(0.0, 1.0, overlap_x, dtype=dtype, device=device).view(1, 1, 1, overlap_x)
323
+ weight[:, :, :, :overlap_x] *= ramp
324
+ if overlap_x > 1 and not touches_right:
325
+ ramp = torch.linspace(1.0, 0.0, overlap_x, dtype=dtype, device=device).view(1, 1, 1, overlap_x)
326
+ weight[:, :, :, -overlap_x:] *= ramp
327
+ return weight
328
+
329
+ def tile_upscale(image, scale=4):
330
+ """Tiled Spandrel upscaling with feathered overlaps to avoid visible seams."""
331
  validate_enhance_input_size(image)
332
  model = load_upscaler_model()
333
  tensor = image_to_tensor(image)
 
360
  x1 = min(x + tile_size, width)
361
  tile = tensor[:, :, y:y1, x:x1]
362
 
363
+ upscaled_tile = model(tile)
364
+ if isinstance(upscaled_tile, (tuple, list)):
365
+ upscaled_tile = upscaled_tile[0]
366
+ upscaled_tile = upscaled_tile.clamp(0, 1)
367
 
368
  scale_y = upscaled_tile.shape[-2] // tile.shape[-2]
369
  scale_x = upscaled_tile.shape[-1] // tile.shape[-1]
 
378
 
379
  oy0, oy1 = y * scale_y, y1 * scale_y
380
  ox0, ox1 = x * scale_x, x1 * scale_x
381
+ blend = _tile_weight(
382
+ upscaled_tile.shape[-2],
383
+ upscaled_tile.shape[-1],
384
+ min(overlap * scale_y, max(1, upscaled_tile.shape[-2] // 2)),
385
+ min(overlap * scale_x, max(1, upscaled_tile.shape[-1] // 2)),
386
+ y == 0,
387
+ y1 == height,
388
+ x == 0,
389
+ x1 == width,
390
+ upscaled_tile.dtype,
391
+ upscaled_tile.device,
392
+ )
393
+ output[:, :, oy0:oy1, ox0:ox1] += upscaled_tile * blend
394
+ weights[:, :, oy0:oy1, ox0:ox1] += blend
395
 
396
  output = output / weights.clamp_min(1)
397
  return tensor_to_image(output)
398
 
399
+ def advanced_tile_upscale(image, scale=4):
400
+ return tile_upscale(image, scale=scale)
401
+
402
  # ============================================================================
403
  # ENHANCED DETAILER (Multi-stage processing)
404
  # ============================================================================
 
469
  return result
470
 
471
  # ============================================================================
472
+ # LEGACY CLEANUP HELPERS
473
  # ============================================================================
474
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
  def remove_artifacts(image):
476
  """Remove compression artifacts and noise"""
477
  denoised = image.filter(ImageFilter.MedianFilter(size=3))
 
541
  array = np.clip(array + grain, 0, 255)
542
  return Image.fromarray(array.astype(np.uint8), mode="RGB")
543
 
544
+ def _resize_to_even_dimensions(width, height):
545
+ return max(2, width - (width % 2)), max(2, height - (height % 2))
546
+
547
+ def qwen_artifact_precondition(image):
548
+ """
549
+ Qwen edit outputs can show halftone/plastic texture at larger dimensions.
550
+ A small Lanczos downsample before the SR pass suppresses that pattern while
551
+ preserving prompt structure for the upscaler.
552
+ """
553
+ base = image.convert("RGB")
554
+ long_edge = max(base.size)
555
+ if long_edge <= ENHANCE_QWEN_DOWNSCALE_TRIGGER_EDGE:
556
+ return base
557
+
558
+ factor = min(0.95, max(0.50, ENHANCE_QWEN_DOWNSCALE_FACTOR))
559
+ new_width, new_height = _resize_to_even_dimensions(
560
+ int(round(base.width * factor)),
561
+ int(round(base.height * factor)),
562
+ )
563
+ if new_width >= base.width or new_height >= base.height:
564
+ return base
565
+ return base.resize((new_width, new_height), Image.Resampling.LANCZOS)
566
+
567
+ def qwen_skin_mask(image):
568
+ base = image.convert("RGB")
569
+ ycbcr = np.asarray(base.convert("YCbCr"))
570
+ y = ycbcr[:, :, 0]
571
+ cb = ycbcr[:, :, 1]
572
+ cr = ycbcr[:, :, 2]
573
+ mask = (
574
+ (y > 45)
575
+ & (cb >= 72)
576
+ & (cb <= 145)
577
+ & (cr >= 128)
578
+ & (cr <= 182)
579
+ ).astype(np.uint8) * 255
580
+
581
+ try:
582
+ import cv2
583
+ kernel = np.ones((3, 3), np.uint8)
584
+ mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
585
+ mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
586
+ mask = cv2.GaussianBlur(mask, (0, 0), 1.6)
587
+ except ImportError:
588
+ mask_image = Image.fromarray(mask, mode="L")
589
+ mask_image = mask_image.filter(ImageFilter.MinFilter(size=3))
590
+ mask_image = mask_image.filter(ImageFilter.MaxFilter(size=5))
591
+ mask_image = mask_image.filter(ImageFilter.GaussianBlur(radius=1.4))
592
+ mask = np.asarray(mask_image)
593
+
594
+ return Image.fromarray(mask.astype(np.uint8), mode="L")
595
+
596
+ def qwen_defect_mask(image):
597
+ base = image.convert("RGB")
598
+ skin = np.asarray(qwen_skin_mask(base)).astype(np.float32) / 255.0
599
+ gray = np.asarray(base.convert("L")).astype(np.float32)
600
+ local = np.asarray(base.convert("L").filter(ImageFilter.MedianFilter(size=5))).astype(np.float32)
601
+ pits = np.maximum(local - gray, 0.0)
602
+ smears = np.abs(gray - local)
603
+ mask = ((pits > 14.0) | (smears > 22.0)) & (skin > 0.12)
604
+ mask = (mask.astype(np.uint8) * 255)
605
+ mask_image = Image.fromarray(mask, mode="L")
606
+ mask_image = mask_image.filter(ImageFilter.MaxFilter(size=3))
607
+ return mask_image.filter(ImageFilter.GaussianBlur(radius=1.1))
608
+
609
+ def masked_texture_repair(image, strength=0.55):
610
+ base = image.convert("RGB")
611
+ mask = qwen_defect_mask(base)
612
+ repaired = base.filter(ImageFilter.MedianFilter(size=3)).filter(ImageFilter.GaussianBlur(radius=0.22))
613
+ repaired = ImageEnhance.Sharpness(repaired).enhance(1.06)
614
+ mask = mask.point(lambda value: int(value * max(0.0, min(1.0, strength))))
615
+ return Image.composite(repaired, base, mask)
616
+
617
+ def qwen_micro_detail(image, amount=0.35):
618
+ if amount <= 0:
619
+ return image.convert("RGB")
620
+ base = image.convert("RGB")
621
+ sharpened = base.filter(ImageFilter.UnsharpMask(radius=0.9, percent=int(95 * amount), threshold=3))
622
+ contrast = ImageEnhance.Contrast(sharpened).enhance(1.0 + amount * 0.05)
623
+ return Image.blend(base, contrast, alpha=min(0.65, amount))
624
+
625
+ def add_photographic_grain(image, seed):
626
+ return add_film_grain(image, seed)
627
+
628
  # ============================================================================
629
  # ENHANCED APPLY ENHANCEMENT (Main enhancement pipeline)
630
  # ============================================================================
631
 
632
  def apply_enhancement(image, enhance_mode, seed=0, progress=None):
633
+ """Apply the Qwen 2511 / Rapid-AIO v23 post-process pipeline."""
 
 
634
  mode = enhance_mode or ENHANCE_MODE_OFF
635
  if mode not in ENHANCE_MODE_CHOICES:
636
  raise gr.Error(f"Unknown enhance mode: {mode}")
 
638
  return image
639
 
640
  enhanced = image.convert("RGB")
641
+ total_steps = {
642
+ ENHANCE_MODE_UPSCALE: 2,
643
+ ENHANCE_MODE_CLEAN: 3,
644
+ ENHANCE_MODE_MAX_DETAIL: 5,
645
+ }[mode]
 
 
 
 
 
 
 
 
646
  step = 0
647
+
648
+ if progress:
649
+ step += 1
650
+ progress(0.85 * step / total_steps, desc="Preparing Qwen output...")
651
+ enhanced = qwen_artifact_precondition(enhanced)
652
+
653
+ if mode in (ENHANCE_MODE_CLEAN, ENHANCE_MODE_MAX_DETAIL):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
  if progress:
655
  step += 1
656
+ progress(0.85 * step / total_steps, desc="Repairing Qwen skin artifacts...")
657
+ enhanced = masked_texture_repair(enhanced, strength=0.55 if mode == ENHANCE_MODE_CLEAN else 0.68)
658
+ enhanced = qwen_micro_detail(enhanced, amount=0.18)
659
+
660
+ if mode == ENHANCE_MODE_MAX_DETAIL:
661
  if progress:
662
  step += 1
663
+ progress(0.85 * step / total_steps, desc="Building micro detail...")
664
+ enhanced = qwen_micro_detail(enhanced, amount=0.36)
665
+
666
+ if progress:
667
+ step += 1
668
+ progress(0.85 * step / total_steps, desc="Upscaling with Nomos tiles...")
669
+ enhanced = tile_upscale(enhanced)
670
+
671
+ if mode == ENHANCE_MODE_MAX_DETAIL:
672
  if progress:
673
  step += 1
674
+ progress(0.98, desc="Adding final photographic grain...")
675
+ enhanced = qwen_micro_detail(enhanced, amount=0.22)
676
+ enhanced = add_photographic_grain(enhanced, seed)
677
+
678
  return enhanced
679
 
680
  # ============================================================================
 
838
  with gr.Blocks(css=css) as demo:
839
  with gr.Column(elem_id="col-container"):
840
  gr.HTML(f"""
841
+ <!-- v{APP_VERSION} -->
842
  <div id="logo-title">
843
  <h1>Pro Realism Edit Studio - Enhanced</h1>
844
+ <h2>Rapid Edit with Qwen-aware Nomos enhancement</h2>
845
  </div>
846
  """)
847
 
 
849
  Powered by:
850
  - [Qwen-Image-Edit-2511](https://huggingface.co/Qwen/Qwen-Image-Edit-2511)
851
  - [Phr00t's Rapid-AIO v23](https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO) accelerated transformer
852
+ - Nomos-family 4x tiled upscaling with masked Qwen artifact repair
 
853
 
854
  Upload an image and enter your prompt to edit it.
855
 
856
  Pro Tips:
857
+ - Use Clean for portraits with smudged or pitted skin
858
+ - Use Max Detail for texture, grain, and sharper final output
 
859
  """)
860
 
861
  with gr.Row():
 
881
  enhance_info = gr.Markdown("""
882
  Enhancement Options:
883
  - Off: No post-processing
884
+ - Upscale: Qwen precondition + 4x Nomos tiled upscale
885
+ - Clean: Masked Qwen artifact repair + upscaling
886
+ - Max Detail: Masked repair + micro detail + upscaling + final grain
 
 
887
  """, visible=False)
888
 
889
  run_button = gr.Button("Generate!", variant="primary")
requirements.txt CHANGED
@@ -16,22 +16,18 @@ Peft
16
  Torchao==0.11.0
17
 
18
  # Image processing and upscaling
19
- Spandrel
20
- Spandrel_extra_arches
21
  Pillow
22
  Numpy
23
 
24
- # Face restoration (optional but recommended)
25
- # Install with: pip install gfpgan
26
- # gfpgan>=1.0.0
27
-
28
- # OpenCV for face detection (optional)
29
- # Install with: pip install opencv-python
30
- # opencv-python>=4.5.0
31
-
32
- # SciPy for advanced image processing (fallback for face detection)
33
- # Install with: pip install scipy
34
- # scipy>=1.7.0
35
 
36
  # Gradio and utilities
37
  Gradio
@@ -45,6 +41,6 @@ Gradio_client
45
  # Performance monitoring (optional)
46
  # Install with: pip install psutil
47
 
48
- # Note: For full functionality, run:
49
- # pip install -r requirements_enhanced.txt
50
- # pip install gfpgan opencv-python scipy
 
16
  Torchao==0.11.0
17
 
18
  # Image processing and upscaling
19
+ spandrel
20
+ spandrel_extra_arches
21
  Pillow
22
  Numpy
23
 
24
+ # OpenCV for faster mask cleanup (optional)
25
+ # Install with: pip install opencv-python
26
+ # opencv-python>=4.5.0
27
+
28
+ # SciPy for legacy image processing helpers (optional)
29
+ # Install with: pip install scipy
30
+ # scipy>=1.7.0
 
 
 
 
31
 
32
  # Gradio and utilities
33
  Gradio
 
41
  # Performance monitoring (optional)
42
  # Install with: pip install psutil
43
 
44
+ # Note: For optional CPU-side mask acceleration, run:
45
+ # pip install -r requirements_enhanced.txt
46
+ # pip install opencv-python scipy