JS6969 commited on
Commit
901db54
Β·
verified Β·
1 Parent(s): 0385957

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -69
app.py CHANGED
@@ -1,56 +1,60 @@
1
- # app.py β€” MjΓΆlnir Β· Upscale Images (Real-ESRGAN)
2
 
3
- # ---- TorchVision shim (keeps basicsr happy if torchvision isn't installed) ----
4
- import sys, types
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  try:
6
  import torchvision.transforms.functional_tensor as _ft # noqa: F401
7
  except Exception:
8
- import torch
9
  _mod = types.ModuleType("torchvision.transforms.functional_tensor")
10
  def rgb_to_grayscale(img: "torch.Tensor", num_output_channels: int = 1) -> "torch.Tensor":
11
  if not torch.is_tensor(img):
12
  raise TypeError("rgb_to_grayscale expects a torch.Tensor")
13
  if img.ndim < 3 or img.shape[-3] != 3:
14
- raise ValueError(f"expected tensor with C=3 as the third-from-last dim, got shape {tuple(img.shape)}")
15
  r, g, b = img[..., -3, :, :], img[..., -2, :, :], img[..., -1, :, :]
16
  gray = 0.2989*r + 0.5870*g + 0.1140*b
17
  return torch.stack([gray, gray, gray], dim=-3) if num_output_channels == 3 else gray.unsqueeze(-3)
18
  _mod.rgb_to_grayscale = rgb_to_grayscale
19
  sys.modules["torchvision.transforms.functional_tensor"] = _mod
20
- # ------------------------------------------------------------------------------
21
 
22
- import os, time, zipfile, tempfile, shutil
23
- from pathlib import Path
24
- from typing import List
25
- import gradio as gr
26
  import numpy as np
27
  import cv2
28
  from PIL import Image
 
29
 
30
- import torch
31
  from basicsr.archs.rrdbnet_arch import RRDBNet as _RRDBNet
32
  from basicsr.utils.download_util import load_file_from_url
33
  from realesrgan import RealESRGANer
34
  from realesrgan.archs.srvgg_arch import SRVGGNetCompact
35
 
36
- # ───────────────────────────────────────────────
37
- # GPU check
38
- # ───────────────────────────────────────────────
39
- def have_gpu() -> bool:
40
- return torch.cuda.is_available()
41
-
42
- if not have_gpu():
43
- print("⚠️ No GPU detected. Upscaling will run on CPU (very slow).")
44
- else:
45
- print(f"βœ… GPU detected: {torch.cuda.get_device_name(0)}")
46
-
47
- # ───────────────────────────────────────────────
48
- # Logo helper
49
- # ───────────────────────────────────────────────
50
  def try_load_logo_b64() -> str:
51
  try:
52
  with open("bifrost_logo.png", "rb") as f:
53
- import base64
54
  return base64.b64encode(f.read()).decode("utf-8")
55
  except Exception:
56
  return ""
@@ -63,17 +67,16 @@ def render_logo_html(px: int = 96) -> str:
63
  {img}
64
  <div>
65
  <div style="font-size:1.6rem;font-weight:800;">MjΓΆlnir Β· Upscale Images</div>
66
- <div style="opacity:0.8;">Real-ESRGAN (batch click with progress)</div>
67
  </div>
68
  </div>
69
  <hr>
70
  """
71
 
72
- # ───────────────────────────────────────────────
73
  # Helpers
74
- # ───────────────────────────────────────────────
75
- import re
76
- _num = re.compile(r'(\d+)')
77
  def _natural_key(p: Path | str):
78
  s = str(p)
79
  return [int(t) if t.isdigit() else t.lower() for t in _num.split(s)]
@@ -97,16 +100,17 @@ def render_progress(pct: float, label: str = "") -> str:
97
  <div style="font-size:12px;opacity:.8;margin-top:4px;">{label} {pct:.1f}%</div>'''
98
 
99
  def build_rrdb(scale: int, num_block: int):
100
- return _RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=num_block, num_grow_ch=32, scale=scale)
 
101
 
102
  def _weights_dir() -> str:
103
  wdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "weights")
104
  os.makedirs(wdir, exist_ok=True)
105
  return wdir
106
 
107
- # ───────────────────────────────────────────────
108
- # FIXED: get_realesrganer (no recursion!)
109
- # ───────────────────────────────────────────────
110
  def get_realesrganer(model_id: str, scale: int, tile: int, half: bool, device: str = "cpu") -> RealESRGANer:
111
  wdir = _weights_dir()
112
  if model_id == "x4plus":
@@ -132,10 +136,8 @@ def get_realesrganer(model_id: str, scale: int, tile: int, half: bool, device: s
132
  if not os.path.isfile(os.path.join(wdir, fname)):
133
  load_file_from_url(url=url, model_dir=wdir, progress=True)
134
 
135
- device = "cuda" if torch.cuda.is_available() else "cpu"
136
- gpu_id = 0 if device == "cuda" else None
137
-
138
- return RealESRGANer(
139
  scale=netscale,
140
  model_path=model_path,
141
  dni_weight=dni_weight,
@@ -143,56 +145,50 @@ def get_realesrganer(model_id: str, scale: int, tile: int, half: bool, device: s
143
  tile=tile or 256,
144
  tile_pad=10,
145
  pre_pad=10,
146
- half=half and (device == "cuda"),
147
- gpu_id=gpu_id,
148
  )
 
149
 
150
- # ───────────────────────────────────────────────
151
- # UI Logic
152
- # ───────────────────────────────────────────────
153
- def map_ui_model_to_internal(ui_name: str) -> str:
154
- return {
155
- "RealESRGAN_x4plus": "x4plus",
156
- "RealESRGAN_x4plus_anime_6B": "x4plus-anime",
157
- "RealESRGAN_x2plus": "x2plus",
158
- "RealESRNet_x4plus": "x4plus",
159
- "realesr-general-x4v3": "x4plus",
160
- }.get(ui_name, "x4plus")
161
-
162
- def clamp_scale_for_model(outscale: int, model_id: str) -> int:
163
- return 2 if model_id == "x2plus" else 4
164
-
165
- def _ensure_dir(p: Path) -> Path:
166
- p.mkdir(parents=True, exist_ok=True); return p
167
-
168
  def _save_zip_of_dir(dir_path: Path, zip_path: Path) -> str:
169
  with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
170
  for p in sorted(dir_path.glob("*.*"), key=_natural_key):
171
  if p.suffix.lower() in [".jpg", ".jpeg", ".png"]:
172
  zf.write(p, p.name)
173
  return str(zip_path)
174
-
175
  def _list_image_paths_from_upload(files: List[gr.File] | None) -> List[str]:
176
  if not files: return []
177
  return [str(Path(f.name)) for f in files if Path(f.name).suffix.lower() in [".jpg",".jpeg",".png"]]
178
-
179
  def _build_gallery_from_dir(dir_path: Path, n: int = 30) -> List[str]:
180
  paths = sorted(list(dir_path.glob("*.jpg")) + list(dir_path.glob("*.png")), key=_natural_key)
181
  return sample_paths(paths, n)
182
 
183
- # (step2_prepare_sources & step2_process_next_batch remain unchanged from your version)
184
- # ───────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
185
 
186
- # [KEEP your step2_prepare_sources and step2_process_next_batch here, unchanged]
187
 
188
- # ───────────────────────────────────────────────
189
- # Build UI
190
- # ───────────────────────────────────────────────
191
  def build_ui():
192
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
193
  gr.HTML(render_logo_html(88))
194
- gr.Markdown("Upload images and upscale with Real-ESRGAN. Process in batches with live progress.")
195
- # ... same UI wiring as before ...
196
  return demo
197
 
198
  if __name__ == "__main__":
 
1
+ # app.py β€” MjΓΆlnir Β· Upscale Images (ZeroGPU safe)
2
 
3
+ # ─────────────────────────────────────────────
4
+ # Force CPU only (ZeroGPU mode, no CUDA allowed)
5
+ # ─────────────────────────────────────────────
6
+ import os, sys, types, time, zipfile, tempfile, shutil, base64
7
+ from pathlib import Path
8
+ from typing import List
9
+
10
+ os.environ["CUDA_VISIBLE_DEVICES"] = "" # hide GPUs completely
11
+
12
+ import torch
13
+ def have_gpu() -> bool:
14
+ return torch.cuda.is_available()
15
+
16
+ if not have_gpu():
17
+ print("⚠️ ZeroGPU mode: Running on CPU only (slow, but stable).")
18
+ else:
19
+ print(f"βœ… GPU detected: {torch.cuda.get_device_name(0)}")
20
+
21
+ # ─────────────────────────────────────────────
22
+ # TorchVision shim (keeps basicsr happy)
23
+ # ─────────────────────────────────────────────
24
  try:
25
  import torchvision.transforms.functional_tensor as _ft # noqa: F401
26
  except Exception:
 
27
  _mod = types.ModuleType("torchvision.transforms.functional_tensor")
28
  def rgb_to_grayscale(img: "torch.Tensor", num_output_channels: int = 1) -> "torch.Tensor":
29
  if not torch.is_tensor(img):
30
  raise TypeError("rgb_to_grayscale expects a torch.Tensor")
31
  if img.ndim < 3 or img.shape[-3] != 3:
32
+ raise ValueError(f"expected tensor with C=3 as the third-from-last dim, got {tuple(img.shape)}")
33
  r, g, b = img[..., -3, :, :], img[..., -2, :, :], img[..., -1, :, :]
34
  gray = 0.2989*r + 0.5870*g + 0.1140*b
35
  return torch.stack([gray, gray, gray], dim=-3) if num_output_channels == 3 else gray.unsqueeze(-3)
36
  _mod.rgb_to_grayscale = rgb_to_grayscale
37
  sys.modules["torchvision.transforms.functional_tensor"] = _mod
 
38
 
39
+ # ─────────────────────────────────────────────
40
+ # Standard libs
41
+ # ─────────────────────────────────────────────
 
42
  import numpy as np
43
  import cv2
44
  from PIL import Image
45
+ import gradio as gr
46
 
 
47
  from basicsr.archs.rrdbnet_arch import RRDBNet as _RRDBNet
48
  from basicsr.utils.download_util import load_file_from_url
49
  from realesrgan import RealESRGANer
50
  from realesrgan.archs.srvgg_arch import SRVGGNetCompact
51
 
52
+ # ─────────────────────────────────────────────
53
+ # Branding
54
+ # ─────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
55
  def try_load_logo_b64() -> str:
56
  try:
57
  with open("bifrost_logo.png", "rb") as f:
 
58
  return base64.b64encode(f.read()).decode("utf-8")
59
  except Exception:
60
  return ""
 
67
  {img}
68
  <div>
69
  <div style="font-size:1.6rem;font-weight:800;">MjΓΆlnir Β· Upscale Images</div>
70
+ <div style="opacity:0.8;">Real-ESRGAN (batch click with progress, ZeroGPU safe)</div>
71
  </div>
72
  </div>
73
  <hr>
74
  """
75
 
76
+ # ─────────────────────────────────────────────
77
  # Helpers
78
+ # ─────────────────────────────────────────────
79
+ _num = __import__("re").compile(r'(\d+)')
 
80
  def _natural_key(p: Path | str):
81
  s = str(p)
82
  return [int(t) if t.isdigit() else t.lower() for t in _num.split(s)]
 
100
  <div style="font-size:12px;opacity:.8;margin-top:4px;">{label} {pct:.1f}%</div>'''
101
 
102
  def build_rrdb(scale: int, num_block: int):
103
+ return _RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64,
104
+ num_block=num_block, num_grow_ch=32, scale=scale)
105
 
106
  def _weights_dir() -> str:
107
  wdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "weights")
108
  os.makedirs(wdir, exist_ok=True)
109
  return wdir
110
 
111
+ # ─────────────────────────────────────────────
112
+ # Real-ESRGAN (CPU only)
113
+ # ─────────────────────────────────────────────
114
  def get_realesrganer(model_id: str, scale: int, tile: int, half: bool, device: str = "cpu") -> RealESRGANer:
115
  wdir = _weights_dir()
116
  if model_id == "x4plus":
 
136
  if not os.path.isfile(os.path.join(wdir, fname)):
137
  load_file_from_url(url=url, model_dir=wdir, progress=True)
138
 
139
+ # πŸ”’ Force CPU only
140
+ upsampler = RealESRGANer(
 
 
141
  scale=netscale,
142
  model_path=model_path,
143
  dni_weight=dni_weight,
 
145
  tile=tile or 256,
146
  tile_pad=10,
147
  pre_pad=10,
148
+ half=False, # CPU cannot use half precision
149
+ gpu_id=None # disable GPU completely
150
  )
151
+ return upsampler
152
 
153
+ # ─────────────────────────────────────────────
154
+ # Step 2: Sources + Processing (batch click)
155
+ # ─────────────────────────────────────────────
156
+ def _ensure_dir(p: Path) -> Path: p.mkdir(parents=True, exist_ok=True); return p
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  def _save_zip_of_dir(dir_path: Path, zip_path: Path) -> str:
158
  with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
159
  for p in sorted(dir_path.glob("*.*"), key=_natural_key):
160
  if p.suffix.lower() in [".jpg", ".jpeg", ".png"]:
161
  zf.write(p, p.name)
162
  return str(zip_path)
 
163
  def _list_image_paths_from_upload(files: List[gr.File] | None) -> List[str]:
164
  if not files: return []
165
  return [str(Path(f.name)) for f in files if Path(f.name).suffix.lower() in [".jpg",".jpeg",".png"]]
 
166
  def _build_gallery_from_dir(dir_path: Path, n: int = 30) -> List[str]:
167
  paths = sorted(list(dir_path.glob("*.jpg")) + list(dir_path.glob("*.png")), key=_natural_key)
168
  return sample_paths(paths, n)
169
 
170
+ def map_ui_model_to_internal(ui_name: str) -> str:
171
+ return {
172
+ "RealESRGAN_x4plus": "x4plus",
173
+ "RealESRGAN_x4plus_anime_6B": "x4plus-anime",
174
+ "RealESRGAN_x2plus": "x2plus",
175
+ "RealESRNet_x4plus": "x4plus",
176
+ "realesr-general-x4v3": "x4plus",
177
+ }.get(ui_name, "x4plus")
178
+
179
+ def clamp_scale_for_model(outscale: int, model_id: str) -> int:
180
+ return 2 if model_id == "x2plus" else 4
181
 
182
+ # (step2_prepare_sources, step2_process_next_batch stay the same as before)
183
 
184
+ # ─────────────────────────────────────────────
185
+ # UI
186
+ # ─────────────────────────────────────────────
187
  def build_ui():
188
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
189
  gr.HTML(render_logo_html(88))
190
+ gr.Markdown("Upload images and upscale with Real-ESRGAN. Runs in CPU-only ZeroGPU mode (slow).")
191
+ # … keep the rest of your batch-click UI wiring unchanged …
192
  return demo
193
 
194
  if __name__ == "__main__":