Madiy commited on
Commit
cbb519a
·
verified ·
1 Parent(s): 673d852

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -15
app.py CHANGED
@@ -136,11 +136,11 @@ enhancer = RealESRGANer(
136
  device=restoration_device,
137
  )
138
 
139
- # 2. GFPGAN Weights Setup
140
- gfpgan_path = f"{weights_dir}/GFPGANv1.3.pth"
141
  ensure_weight_file(
142
  gfpgan_path,
143
- "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth",
144
  )
145
 
146
  gfpgan_restorer = GFPGANer(
@@ -227,6 +227,48 @@ def cap_image(image, max_side=1024):
227
  return image.resize((int(image.width * ratio), int(image.height * ratio)), Image.LANCZOS)
228
 
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  def _colorfulness(image):
231
  rgb = np.array(image.convert("RGB"))
232
  R, G, B = rgb[:, :, 0], rgb[:, :, 1], rgb[:, :, 2]
@@ -258,17 +300,34 @@ def enhance_image(image, scale_factor, mode="Pure Super-Resolution (Safe for Gro
258
 
259
  try:
260
  if "Pure Super-Resolution" in mode:
261
- # Pure Real-ESRGAN Upscaling & Deblurring (preserves exact facial features and group structures)
262
  output_bgr, _ = enhancer.enhance(input_bgr, outscale=target_scale)
263
  else:
264
- # GFPGAN Face Deblurring + Real-ESRGAN Canvas Upscaling
 
 
 
 
265
  gfpgan_restorer.upscale = target_scale
266
- _, _, output_bgr = gfpgan_restorer.enhance(
267
  input_bgr,
268
  has_aligned=False,
269
  only_center_face=False,
270
  paste_back=True,
271
- weight=float(fidelity_weight)
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  )
273
 
274
  output_rgb = cv2.cvtColor(output_bgr, cv2.COLOR_BGR2RGB)
@@ -600,11 +659,37 @@ with gr.Blocks(
600
 
601
  # Enhance Tab UI
602
  with gr.Tab("✨ Enhance", id="tab_enhance"):
603
- gr.Markdown("## Restore old blurry photos, sharpen features, and upscale up to 4x.")
 
 
 
 
604
  with gr.Row(equal_height=True):
605
  with gr.Column(scale=1):
606
  enh_input = gr.Image(label="Upload Image", type="pil", height=350)
607
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
  enh_mode = gr.Radio(
609
  choices=[
610
  "Pure Super-Resolution (Safe for Group Photos)",
@@ -616,8 +701,11 @@ with gr.Blocks(
616
 
617
  enh_scale = gr.Radio(choices=["2x", "4x"], value="4x", label="Upscale Factor")
618
  enh_weight = gr.Slider(
619
- minimum=0.0, maximum=0.5, value=0.15, step=0.05,
620
- label="Face Restoration Weight (Only applies in Refinement mode; keep low <= 0.2)"
 
 
 
621
  )
622
  enh_metrics = gr.Checkbox(label="📊 Compute quality metrics", value=False)
623
  enh_btn = gr.Button("Enhance Image", variant="primary", size="lg")
@@ -625,10 +713,27 @@ with gr.Blocks(
625
  enh_output = gr.Image(label="Result", type="pil", interactive=False, height=350)
626
  enh_info = gr.Textbox(label="Size Info + Metrics", interactive=False, lines=4)
627
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
  enh_btn.click(
629
- fn=enhance_image,
630
- inputs=[enh_input, enh_scale, enh_mode, enh_weight, enh_metrics],
631
- outputs=[enh_output, enh_info]
632
  )
633
 
634
  # Inter-tab Event Routing Connections
@@ -636,4 +741,4 @@ with gr.Blocks(
636
  btn_col_to_enhance.click(fn=send_to_enhance, inputs=[col_output], outputs=[enh_input, tabs])
637
  btn_out_to_enhance.click(fn=send_to_enhance, inputs=[out_output], outputs=[enh_input, tabs])
638
 
639
- demo.queue().launch(show_error=True)
 
136
  device=restoration_device,
137
  )
138
 
139
+ # 2. GFPGAN Weights Setup — version 1.4
140
+ gfpgan_path = f"{weights_dir}/GFPGANv1.4.pth"
141
  ensure_weight_file(
142
  gfpgan_path,
143
+ "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth",
144
  )
145
 
146
  gfpgan_restorer = GFPGANer(
 
227
  return image.resize((int(image.width * ratio), int(image.height * ratio)), Image.LANCZOS)
228
 
229
 
230
+ def cleanup_vintage_image(image, scratch_mask=None, denoise_strength=0):
231
+ """CPU-only noise cleanup and optional user-mask-guided scratch repair.
232
+
233
+ Paint/prepare the mask with white over defects to repair and black elsewhere.
234
+ OpenCV inpainting works best on thin scratches and small blemishes, not large
235
+ missing areas. A denoise value of zero disables denoising.
236
+ """
237
+ if image is None:
238
+ raise gr.Error("Please upload an image first.")
239
+
240
+ image = cap_image(image, 1200)
241
+ rgb = np.asarray(image.convert("RGB"), dtype=np.uint8)
242
+
243
+ if scratch_mask is not None:
244
+ mask = np.asarray(
245
+ scratch_mask.convert("L").resize(image.size, Image.NEAREST),
246
+ dtype=np.uint8,
247
+ )
248
+ _, mask = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)
249
+ if np.any(mask):
250
+ # Expand slightly to cover the edge of a painted scratch mask.
251
+ mask = cv2.dilate(mask, np.ones((3, 3), dtype=np.uint8), iterations=1)
252
+ bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)
253
+ bgr = cv2.inpaint(bgr, mask, 3, cv2.INPAINT_TELEA)
254
+ rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
255
+
256
+ denoise_strength = int(denoise_strength)
257
+ if not 0 <= denoise_strength <= 10:
258
+ raise gr.Error("Denoise strength must be between 0 and 10.")
259
+ if denoise_strength > 0:
260
+ rgb = cv2.fastNlMeansDenoisingColored(
261
+ rgb,
262
+ None,
263
+ float(denoise_strength),
264
+ float(denoise_strength),
265
+ 7,
266
+ 21,
267
+ )
268
+
269
+ return Image.fromarray(rgb)
270
+
271
+
272
  def _colorfulness(image):
273
  rgb = np.array(image.convert("RGB"))
274
  R, G, B = rgb[:, :, 0], rgb[:, :, 1], rgb[:, :, 2]
 
300
 
301
  try:
302
  if "Pure Super-Resolution" in mode:
303
+ # Real-ESRGAN upscaling only; this mode does not run GFPGAN face restoration.
304
  output_bgr, _ = enhancer.enhance(input_bgr, outscale=target_scale)
305
  else:
306
+ # GFPGAN restores detected faces; Real-ESRGAN upsamples the canvas.
307
+ # In GFPGAN's clean architecture the `weight` kwarg is not used for
308
+ # blending, so blend explicitly here for consistent UI behavior:
309
+ # 0.0 = full restored result; larger values preserve more of the
310
+ # upscaled input and reduce the visible restoration effect.
311
  gfpgan_restorer.upscale = target_scale
312
+ _, _, restored_bgr = gfpgan_restorer.enhance(
313
  input_bgr,
314
  has_aligned=False,
315
  only_center_face=False,
316
  paste_back=True,
317
+ weight=0.0,
318
+ )
319
+ fidelity = float(np.clip(fidelity_weight, 0.0, 0.7))
320
+ input_upscaled = cv2.resize(
321
+ input_bgr,
322
+ (restored_bgr.shape[1], restored_bgr.shape[0]),
323
+ interpolation=cv2.INTER_LANCZOS4,
324
+ )
325
+ output_bgr = cv2.addWeighted(
326
+ restored_bgr,
327
+ 1.0 - fidelity,
328
+ input_upscaled,
329
+ fidelity,
330
+ 0.0,
331
  )
332
 
333
  output_rgb = cv2.cvtColor(output_bgr, cv2.COLOR_BGR2RGB)
 
659
 
660
  # Enhance Tab UI
661
  with gr.Tab("✨ Enhance", id="tab_enhance"):
662
+ gr.Markdown(
663
+ "## Restore faces, clean noise/small scratches, and upscale up to 4x.\n"
664
+ "The cleanup tool is CPU/OpenCV-based. Upload a grayscale mask with "
665
+ "white over scratches and black elsewhere."
666
+ )
667
  with gr.Row(equal_height=True):
668
  with gr.Column(scale=1):
669
  enh_input = gr.Image(label="Upload Image", type="pil", height=350)
670
+
671
+ with gr.Accordion("Vintage Cleanup (CPU / OpenCV)", open=False):
672
+ scratch_mask_input = gr.Image(
673
+ label="Optional scratch mask (white = repair, black = keep)",
674
+ type="pil",
675
+ height=220,
676
+ )
677
+ denoise_strength = gr.Slider(
678
+ minimum=0,
679
+ maximum=10,
680
+ value=0,
681
+ step=1,
682
+ label="Noise reduction (0 = off; higher can soften detail)",
683
+ )
684
+ cleanup_btn = gr.Button("Clean / Repair Image", variant="secondary")
685
+ cleanup_preview = gr.Image(
686
+ label="Cleaned Preview (use below to continue)",
687
+ type="pil",
688
+ interactive=False,
689
+ height=300,
690
+ )
691
+ use_cleaned_btn = gr.Button("Use Cleaned Image for Enhancement")
692
+
693
  enh_mode = gr.Radio(
694
  choices=[
695
  "Pure Super-Resolution (Safe for Group Photos)",
 
701
 
702
  enh_scale = gr.Radio(choices=["2x", "4x"], value="4x", label="Upscale Factor")
703
  enh_weight = gr.Slider(
704
+ minimum=0.0,
705
+ maximum=0.7,
706
+ value=0.15,
707
+ step=0.05,
708
+ label="Original preservation (0 = strongest face restoration; higher = more original)",
709
  )
710
  enh_metrics = gr.Checkbox(label="📊 Compute quality metrics", value=False)
711
  enh_btn = gr.Button("Enhance Image", variant="primary", size="lg")
 
713
  enh_output = gr.Image(label="Result", type="pil", interactive=False, height=350)
714
  enh_info = gr.Textbox(label="Size Info + Metrics", interactive=False, lines=4)
715
 
716
+ cleanup_btn.click(
717
+ fn=cleanup_vintage_image,
718
+ inputs=[enh_input, scratch_mask_input, denoise_strength],
719
+ outputs=[cleanup_preview],
720
+ )
721
+
722
+ def use_cleaned_image(image):
723
+ if image is None:
724
+ raise gr.Error("Run cleanup first, or upload an image.")
725
+ return image
726
+
727
+ use_cleaned_btn.click(
728
+ fn=use_cleaned_image,
729
+ inputs=[cleanup_preview],
730
+ outputs=[enh_input],
731
+ )
732
+
733
  enh_btn.click(
734
+ fn=enhance_image,
735
+ inputs=[enh_input, enh_scale, enh_mode, enh_weight, enh_metrics],
736
+ outputs=[enh_output, enh_info],
737
  )
738
 
739
  # Inter-tab Event Routing Connections
 
741
  btn_col_to_enhance.click(fn=send_to_enhance, inputs=[col_output], outputs=[enh_input, tabs])
742
  btn_out_to_enhance.click(fn=send_to_enhance, inputs=[out_output], outputs=[enh_input, tabs])
743
 
744
+ demo.queue().launch(show_error=True)