Upload 2 files
Browse files- app.py +58 -147
- requirements.txt +1 -4
app.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import torch
|
| 3 |
-
from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_upscale import StableDiffusionUpscalePipeline
|
| 4 |
-
from transformers import AutoImageProcessor, Swin2SRForImageSuperResolution
|
| 5 |
import gc
|
| 6 |
from PIL import Image
|
| 7 |
import numpy as np
|
|
@@ -22,8 +20,6 @@ class Config:
|
|
| 22 |
MODEL_DIR = "weights"
|
| 23 |
REALESRGAN_URL = "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth"
|
| 24 |
REALESRGAN_FILENAME = "RealESRGAN_x2plus.pth"
|
| 25 |
-
SWIN2SR_ID = "caidas/swin2SR-classical-sr-x2-64"
|
| 26 |
-
SD_ID = "stabilityai/stable-diffusion-x4-upscaler"
|
| 27 |
|
| 28 |
# SOTA Models (2025)
|
| 29 |
SPAN_URL = "https://huggingface.co/Phips/2xNomosUni_span_multijpg/resolve/main/2xNomosUni_span_multijpg.safetensors"
|
|
@@ -31,7 +27,6 @@ class Config:
|
|
| 31 |
HATS_URL = "https://huggingface.co/Phips/4xNomos8kSCHAT-S/resolve/main/4xNomos8kSCHAT-S.safetensors"
|
| 32 |
HATS_FILENAME = "4xNomos8kSCHAT-S.safetensors"
|
| 33 |
|
| 34 |
-
MAX_IMAGE_SIZE_SD = 512 # Max dimension for SD input to prevent OOM
|
| 35 |
DEVICE = "cpu" # Force CPU for this demo, can be "cuda" if available
|
| 36 |
|
| 37 |
@staticmethod
|
|
@@ -193,22 +188,22 @@ class RealESRGANStrategy(UpscalerStrategy):
|
|
| 193 |
# 'reduce-overhead' uses CUDA graphs, so only use it on CUDA
|
| 194 |
if Config.DEVICE == 'cuda':
|
| 195 |
self.model = torch.compile(self.model, mode='reduce-overhead')
|
| 196 |
-
logger.info("
|
| 197 |
elif os.name == 'nt' and Config.DEVICE == 'cpu':
|
| 198 |
# Windows requires MSVC for Inductor (default cpu backend)
|
| 199 |
# We skip it to avoid "Compiler: cl is not found" error unless user has it.
|
| 200 |
-
logger.info("
|
| 201 |
elif (psutil.cpu_count(logical=False) or 0) < 4 and Config.DEVICE == 'cpu':
|
| 202 |
# Skip compilation on weak CPUs (e.g. HF Spaces Free Tier) to avoid long startup times
|
| 203 |
-
logger.info("
|
| 204 |
else:
|
| 205 |
# On Linux/Mac CPU, use default mode or skip if problematic. Default is usually safe.
|
| 206 |
self.model = torch.compile(self.model)
|
| 207 |
-
logger.info("
|
| 208 |
|
| 209 |
self.compiled = True
|
| 210 |
except Exception as e:
|
| 211 |
-
logger.warning(f"
|
| 212 |
self.compiled = True # Mark as tried
|
| 213 |
|
| 214 |
logger.info(f"{self.name} loaded successfully.")
|
|
@@ -270,102 +265,6 @@ class RealESRGANStrategy(UpscalerStrategy):
|
|
| 270 |
|
| 271 |
return Image.fromarray(output_np)
|
| 272 |
|
| 273 |
-
class Swin2SRStrategy(UpscalerStrategy):
|
| 274 |
-
def __init__(self):
|
| 275 |
-
super().__init__()
|
| 276 |
-
self.name = "Swin2SR x2"
|
| 277 |
-
self.processor = None
|
| 278 |
-
|
| 279 |
-
def load(self) -> None:
|
| 280 |
-
if self.model is None:
|
| 281 |
-
logger.info(f"Loading {self.name}...")
|
| 282 |
-
try:
|
| 283 |
-
self.processor = AutoImageProcessor.from_pretrained(Config.SWIN2SR_ID)
|
| 284 |
-
model = Swin2SRForImageSuperResolution.from_pretrained(Config.SWIN2SR_ID)
|
| 285 |
-
self.model = model.to(Config.DEVICE) # type: ignore
|
| 286 |
-
logger.info(f"{self.name} loaded successfully.")
|
| 287 |
-
except Exception as e:
|
| 288 |
-
logger.error(f"Failed to load Swin2SR: {e}")
|
| 289 |
-
# Swin2SR loading failure is often due to transformers version mismatch or device issues
|
| 290 |
-
# We re-raise to let the UI handle it, but log the specific error
|
| 291 |
-
raise
|
| 292 |
-
|
| 293 |
-
def upscale(self, image: Image.Image, **kwargs) -> Image.Image:
|
| 294 |
-
if self.model is None or self.processor is None:
|
| 295 |
-
self.load()
|
| 296 |
-
|
| 297 |
-
logger.info(f"Starting inference with {self.name}...")
|
| 298 |
-
start_time = time.time()
|
| 299 |
-
|
| 300 |
-
if self.processor is None:
|
| 301 |
-
raise ValueError("Processor not loaded")
|
| 302 |
-
|
| 303 |
-
inputs = self.processor(images=image, return_tensors="pt").to(Config.DEVICE)
|
| 304 |
-
|
| 305 |
-
# Swin2SR on CPU can be finicky with autocast/tracing.
|
| 306 |
-
# Explicitly disable autocast for Swin2SR on CPU to avoid "PythonFallbackKernel" errors
|
| 307 |
-
context = torch.no_grad()
|
| 308 |
-
|
| 309 |
-
with context:
|
| 310 |
-
outputs = self.model(**inputs)
|
| 311 |
-
|
| 312 |
-
output = outputs.reconstruction.data.squeeze().float().cpu().clamp_(0, 1).numpy()
|
| 313 |
-
output = np.moveaxis(output, source=0, destination=-1)
|
| 314 |
-
output = (output * 255.0).round().astype(np.uint8)
|
| 315 |
-
|
| 316 |
-
logger.info(f"Inference finished in {time.time() - start_time:.2f}s")
|
| 317 |
-
return Image.fromarray(output)
|
| 318 |
-
|
| 319 |
-
class StableDiffusionStrategy(UpscalerStrategy):
|
| 320 |
-
def __init__(self):
|
| 321 |
-
super().__init__()
|
| 322 |
-
self.name = "Stable Diffusion x4"
|
| 323 |
-
|
| 324 |
-
def load(self) -> None:
|
| 325 |
-
if self.model is None:
|
| 326 |
-
logger.info(f"Loading {self.name} (this may take time)...")
|
| 327 |
-
try:
|
| 328 |
-
self.model = StableDiffusionUpscalePipeline.from_pretrained(
|
| 329 |
-
Config.SD_ID,
|
| 330 |
-
torch_dtype=torch.float32,
|
| 331 |
-
low_cpu_mem_usage=True
|
| 332 |
-
)
|
| 333 |
-
# Optimizations for CPU
|
| 334 |
-
self.model.enable_attention_slicing("max")
|
| 335 |
-
self.model.enable_vae_tiling()
|
| 336 |
-
logger.info(f"{self.name} loaded successfully.")
|
| 337 |
-
except Exception as e:
|
| 338 |
-
logger.error(f"Failed to load Stable Diffusion: {e}")
|
| 339 |
-
raise
|
| 340 |
-
|
| 341 |
-
def upscale(self, image: Image.Image, **kwargs) -> Image.Image:
|
| 342 |
-
if self.model is None:
|
| 343 |
-
self.load()
|
| 344 |
-
|
| 345 |
-
prompt = kwargs.get("prompt", "high quality, detailed")
|
| 346 |
-
|
| 347 |
-
# Pre-check size
|
| 348 |
-
if max(image.size) > Config.MAX_IMAGE_SIZE_SD:
|
| 349 |
-
ratio = Config.MAX_IMAGE_SIZE_SD / max(image.size)
|
| 350 |
-
new_size = (int(image.size[0] * ratio), int(image.size[1] * ratio))
|
| 351 |
-
image = image.resize(new_size, Image.Resampling.LANCZOS)
|
| 352 |
-
logger.warning(f"Resized input to {new_size} to prevent OOM on CPU.")
|
| 353 |
-
|
| 354 |
-
logger.info(f"Starting inference with {self.name}...")
|
| 355 |
-
start_time = time.time()
|
| 356 |
-
|
| 357 |
-
generator = torch.manual_seed(42)
|
| 358 |
-
output = self.model(
|
| 359 |
-
prompt=prompt,
|
| 360 |
-
image=image,
|
| 361 |
-
num_inference_steps=20,
|
| 362 |
-
guidance_scale=7.0,
|
| 363 |
-
generator=generator
|
| 364 |
-
).images[0] # type: ignore
|
| 365 |
-
|
| 366 |
-
logger.info(f"Inference finished in {time.time() - start_time:.2f}s")
|
| 367 |
-
return output
|
| 368 |
-
|
| 369 |
class SpanStrategy(UpscalerStrategy):
|
| 370 |
def __init__(self):
|
| 371 |
super().__init__()
|
|
@@ -401,14 +300,14 @@ class SpanStrategy(UpscalerStrategy):
|
|
| 401 |
try:
|
| 402 |
if Config.DEVICE == 'cuda':
|
| 403 |
self.model = torch.compile(self.model, mode='reduce-overhead')
|
| 404 |
-
logger.info("
|
| 405 |
elif os.name == 'nt' and Config.DEVICE == 'cpu':
|
| 406 |
-
logger.info("
|
| 407 |
elif (psutil.cpu_count(logical=False) or 0) < 4 and Config.DEVICE == 'cpu':
|
| 408 |
-
logger.info("
|
| 409 |
else:
|
| 410 |
# SPAN architecture uses .data.clone() in forward pass which breaks torch.compile/inductor
|
| 411 |
-
logger.info("
|
| 412 |
# self.model = torch.compile(self.model)
|
| 413 |
self.compiled = True
|
| 414 |
except Exception:
|
|
@@ -503,7 +402,7 @@ class HatsStrategy(UpscalerStrategy):
|
|
| 503 |
pass
|
| 504 |
else:
|
| 505 |
# HAT architecture also triggers "UntypedStorage" weakref errors with inductor on CPU
|
| 506 |
-
logger.info("
|
| 507 |
# self.model = torch.compile(self.model)
|
| 508 |
self.compiled = True
|
| 509 |
except Exception:
|
|
@@ -560,9 +459,7 @@ class UpscalerManager:
|
|
| 560 |
self.strategies: Dict[str, UpscalerStrategy] = {
|
| 561 |
"SPAN (NomosUni) x2": SpanStrategy(),
|
| 562 |
"RealESRGAN x2": RealESRGANStrategy(),
|
| 563 |
-
"HAT-S x4": HatsStrategy()
|
| 564 |
-
"Swin2SR x2": Swin2SRStrategy(),
|
| 565 |
-
"Stable Diffusion x4": StableDiffusionStrategy()
|
| 566 |
}
|
| 567 |
self.current_model_name: Optional[str] = None
|
| 568 |
|
|
@@ -590,26 +487,31 @@ class UpscalerManager:
|
|
| 590 |
manager = UpscalerManager()
|
| 591 |
|
| 592 |
# --- Gradio Interface Logic ---
|
| 593 |
-
def process_image(input_img: Image.Image, model_name: str,
|
| 594 |
if input_img is None:
|
| 595 |
return None, get_logs(), get_system_usage()
|
| 596 |
|
| 597 |
try:
|
| 598 |
strategy = manager.get_strategy(model_name)
|
| 599 |
|
| 600 |
-
|
| 601 |
-
# For now, we just rely on the user or OS, but in prod we might auto-unload.
|
| 602 |
|
| 603 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
|
| 605 |
# Explicit GC after heavy operations
|
| 606 |
gc.collect()
|
| 607 |
|
| 608 |
-
return
|
| 609 |
except Exception as e:
|
| 610 |
error_msg = f"Critical Error: {str(e)}\n{traceback.format_exc()}"
|
| 611 |
logger.error(error_msg)
|
| 612 |
-
# Return the error message in the logs output so the user sees it
|
| 613 |
return None, get_logs() + "\n\n" + error_msg, get_system_usage()
|
| 614 |
|
| 615 |
def unload_models():
|
|
@@ -618,49 +520,61 @@ def unload_models():
|
|
| 618 |
|
| 619 |
# --- UI Construction ---
|
| 620 |
desc = """
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 628 |
"""
|
| 629 |
|
| 630 |
with gr.Blocks(title="Universal Upscaler Pro") as iface:
|
| 631 |
gr.Markdown(desc)
|
| 632 |
|
| 633 |
with gr.Row():
|
| 634 |
-
with gr.Column(scale=1):
|
| 635 |
-
input_image = gr.Image(type="pil", label="Input Image")
|
| 636 |
|
| 637 |
-
with gr.
|
| 638 |
model_selector = gr.Dropdown(
|
| 639 |
choices=list(manager.strategies.keys()),
|
| 640 |
value="SPAN (NomosUni) x2",
|
| 641 |
-
label="
|
|
|
|
| 642 |
)
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
value="
|
| 646 |
-
|
|
|
|
| 647 |
)
|
| 648 |
|
|
|
|
|
|
|
| 649 |
with gr.Accordion("Advanced Settings", open=False):
|
| 650 |
-
gr.Markdown("Memory Management")
|
| 651 |
unload_btn = gr.Button("Unload All Models (Free RAM)", variant="secondary")
|
| 652 |
-
|
| 653 |
-
submit_btn = gr.Button("✨ Upscale Image", variant="primary", size="lg")
|
| 654 |
-
system_info = gr.Label(value=get_system_usage(), label="System Status")
|
| 655 |
|
| 656 |
-
with gr.Column(scale=1):
|
| 657 |
-
output_image = gr.Image(type="
|
| 658 |
-
logs_output = gr.TextArea(label="Execution Logs", interactive=False, lines=
|
| 659 |
|
| 660 |
# Event Wiring
|
| 661 |
submit_btn.click(
|
| 662 |
fn=process_image,
|
| 663 |
-
inputs=[input_image, model_selector,
|
| 664 |
outputs=[output_image, logs_output, system_info]
|
| 665 |
)
|
| 666 |
|
|
@@ -669,8 +583,5 @@ with gr.Blocks(title="Universal Upscaler Pro") as iface:
|
|
| 669 |
inputs=[],
|
| 670 |
outputs=[logs_output, system_info]
|
| 671 |
)
|
| 672 |
-
|
| 673 |
-
# Auto-refresh system info every 2 seconds (optional, can be heavy on UI)
|
| 674 |
-
# iface.load(get_system_usage, None, system_info, every=2)
|
| 675 |
|
| 676 |
-
iface.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import torch
|
|
|
|
|
|
|
| 3 |
import gc
|
| 4 |
from PIL import Image
|
| 5 |
import numpy as np
|
|
|
|
| 20 |
MODEL_DIR = "weights"
|
| 21 |
REALESRGAN_URL = "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth"
|
| 22 |
REALESRGAN_FILENAME = "RealESRGAN_x2plus.pth"
|
|
|
|
|
|
|
| 23 |
|
| 24 |
# SOTA Models (2025)
|
| 25 |
SPAN_URL = "https://huggingface.co/Phips/2xNomosUni_span_multijpg/resolve/main/2xNomosUni_span_multijpg.safetensors"
|
|
|
|
| 27 |
HATS_URL = "https://huggingface.co/Phips/4xNomos8kSCHAT-S/resolve/main/4xNomos8kSCHAT-S.safetensors"
|
| 28 |
HATS_FILENAME = "4xNomos8kSCHAT-S.safetensors"
|
| 29 |
|
|
|
|
| 30 |
DEVICE = "cpu" # Force CPU for this demo, can be "cuda" if available
|
| 31 |
|
| 32 |
@staticmethod
|
|
|
|
| 188 |
# 'reduce-overhead' uses CUDA graphs, so only use it on CUDA
|
| 189 |
if Config.DEVICE == 'cuda':
|
| 190 |
self.model = torch.compile(self.model, mode='reduce-overhead')
|
| 191 |
+
logger.info("[INFO] torch.compile enabled (reduce-overhead mode)")
|
| 192 |
elif os.name == 'nt' and Config.DEVICE == 'cpu':
|
| 193 |
# Windows requires MSVC for Inductor (default cpu backend)
|
| 194 |
# We skip it to avoid "Compiler: cl is not found" error unless user has it.
|
| 195 |
+
logger.info("[INFO] Skipping torch.compile on Windows CPU to avoid MSVC requirement.")
|
| 196 |
elif (psutil.cpu_count(logical=False) or 0) < 4 and Config.DEVICE == 'cpu':
|
| 197 |
# Skip compilation on weak CPUs (e.g. HF Spaces Free Tier) to avoid long startup times
|
| 198 |
+
logger.info("[INFO] Skipping torch.compile on low-core CPU to prevent timeout.")
|
| 199 |
else:
|
| 200 |
# On Linux/Mac CPU, use default mode or skip if problematic. Default is usually safe.
|
| 201 |
self.model = torch.compile(self.model)
|
| 202 |
+
logger.info("[SUCCESS] torch.compile enabled (default mode)")
|
| 203 |
|
| 204 |
self.compiled = True
|
| 205 |
except Exception as e:
|
| 206 |
+
logger.warning(f"[WARNING] torch.compile not available or failed: {e}")
|
| 207 |
self.compiled = True # Mark as tried
|
| 208 |
|
| 209 |
logger.info(f"{self.name} loaded successfully.")
|
|
|
|
| 265 |
|
| 266 |
return Image.fromarray(output_np)
|
| 267 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
class SpanStrategy(UpscalerStrategy):
|
| 269 |
def __init__(self):
|
| 270 |
super().__init__()
|
|
|
|
| 300 |
try:
|
| 301 |
if Config.DEVICE == 'cuda':
|
| 302 |
self.model = torch.compile(self.model, mode='reduce-overhead')
|
| 303 |
+
logger.info("[INFO] torch.compile enabled (reduce-overhead mode)")
|
| 304 |
elif os.name == 'nt' and Config.DEVICE == 'cpu':
|
| 305 |
+
logger.info("[INFO] Skipping torch.compile on Windows CPU.")
|
| 306 |
elif (psutil.cpu_count(logical=False) or 0) < 4 and Config.DEVICE == 'cpu':
|
| 307 |
+
logger.info("[INFO] Skipping torch.compile on low-core CPU.")
|
| 308 |
else:
|
| 309 |
# SPAN architecture uses .data.clone() in forward pass which breaks torch.compile/inductor
|
| 310 |
+
logger.info("[INFO] Skipping torch.compile for SPAN (incompatible architecture).")
|
| 311 |
# self.model = torch.compile(self.model)
|
| 312 |
self.compiled = True
|
| 313 |
except Exception:
|
|
|
|
| 402 |
pass
|
| 403 |
else:
|
| 404 |
# HAT architecture also triggers "UntypedStorage" weakref errors with inductor on CPU
|
| 405 |
+
logger.info("[INFO] Skipping torch.compile for HAT-S (incompatible architecture).")
|
| 406 |
# self.model = torch.compile(self.model)
|
| 407 |
self.compiled = True
|
| 408 |
except Exception:
|
|
|
|
| 459 |
self.strategies: Dict[str, UpscalerStrategy] = {
|
| 460 |
"SPAN (NomosUni) x2": SpanStrategy(),
|
| 461 |
"RealESRGAN x2": RealESRGANStrategy(),
|
| 462 |
+
"HAT-S x4": HatsStrategy()
|
|
|
|
|
|
|
| 463 |
}
|
| 464 |
self.current_model_name: Optional[str] = None
|
| 465 |
|
|
|
|
| 487 |
manager = UpscalerManager()
|
| 488 |
|
| 489 |
# --- Gradio Interface Logic ---
|
| 490 |
+
def process_image(input_img: Image.Image, model_name: str, output_format: str) -> Tuple[Optional[str], str, str]:
|
| 491 |
if input_img is None:
|
| 492 |
return None, get_logs(), get_system_usage()
|
| 493 |
|
| 494 |
try:
|
| 495 |
strategy = manager.get_strategy(model_name)
|
| 496 |
|
| 497 |
+
output_img = strategy.upscale(input_img)
|
|
|
|
| 498 |
|
| 499 |
+
# Save to temp file with correct extension
|
| 500 |
+
output_path = f"output.{output_format.lower()}"
|
| 501 |
+
|
| 502 |
+
# Convert to RGB if saving as JPEG (doesn't support alpha)
|
| 503 |
+
if output_format.lower() in ['jpeg', 'jpg'] and output_img.mode == 'RGBA':
|
| 504 |
+
output_img = output_img.convert('RGB')
|
| 505 |
+
|
| 506 |
+
output_img.save(output_path, format=output_format)
|
| 507 |
|
| 508 |
# Explicit GC after heavy operations
|
| 509 |
gc.collect()
|
| 510 |
|
| 511 |
+
return output_path, get_logs(), get_system_usage()
|
| 512 |
except Exception as e:
|
| 513 |
error_msg = f"Critical Error: {str(e)}\n{traceback.format_exc()}"
|
| 514 |
logger.error(error_msg)
|
|
|
|
| 515 |
return None, get_logs() + "\n\n" + error_msg, get_system_usage()
|
| 516 |
|
| 517 |
def unload_models():
|
|
|
|
| 520 |
|
| 521 |
# --- UI Construction ---
|
| 522 |
desc = """
|
| 523 |
+
# Universal Upscaler Pro (CPU Optimized)
|
| 524 |
+
|
| 525 |
+
This application provides state-of-the-art (SOTA) image upscaling running entirely on CPU, optimized for free-tier cloud environments.
|
| 526 |
+
|
| 527 |
+
### Available Models
|
| 528 |
+
|
| 529 |
+
| Model | Scale | Best For | License |
|
| 530 |
+
| :--- | :--- | :--- | :--- |
|
| 531 |
+
| **SPAN (NomosUni)** | x2 | **Speed & General Use**. Extremely fast, parameter-free attention network. | Apache 2.0 |
|
| 532 |
+
| **RealESRGAN** | x2 | **Robustness**. Excellent at removing JPEG artifacts and noise. | BSD 3-Clause |
|
| 533 |
+
| **HAT-S** | x4 | **Texture Detail**. Hybrid Attention Transformer for high-fidelity restoration. | MIT |
|
| 534 |
+
|
| 535 |
+
### Attributions & Credits
|
| 536 |
+
|
| 537 |
+
* **Real-ESRGAN**: [Wang et al., 2021](https://github.com/xinntao/Real-ESRGAN). *Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data*.
|
| 538 |
+
* **SPAN**: [Zhang et al., 2023](https://github.com/hongyuanyu/SPAN). *Swift Parameter-free Attention Network for Efficient Super-Resolution*.
|
| 539 |
+
* **HAT**: [Chen et al., 2023](https://github.com/XPixelGroup/HAT). *Activating Activation Functions for Image Restoration*.
|
| 540 |
+
* **NomosUni**: Custom SPAN training by [Phhofm](https://github.com/Phhofm).
|
| 541 |
"""
|
| 542 |
|
| 543 |
with gr.Blocks(title="Universal Upscaler Pro") as iface:
|
| 544 |
gr.Markdown(desc)
|
| 545 |
|
| 546 |
with gr.Row():
|
| 547 |
+
with gr.Column(scale=1, min_width=300):
|
| 548 |
+
input_image = gr.Image(type="pil", label="Input Image", height=400)
|
| 549 |
|
| 550 |
+
with gr.Row():
|
| 551 |
model_selector = gr.Dropdown(
|
| 552 |
choices=list(manager.strategies.keys()),
|
| 553 |
value="SPAN (NomosUni) x2",
|
| 554 |
+
label="Model Architecture",
|
| 555 |
+
scale=2
|
| 556 |
)
|
| 557 |
+
output_format = gr.Dropdown(
|
| 558 |
+
choices=["PNG", "JPEG", "WEBP"],
|
| 559 |
+
value="PNG",
|
| 560 |
+
label="Output Format",
|
| 561 |
+
scale=1
|
| 562 |
)
|
| 563 |
|
| 564 |
+
submit_btn = gr.Button("Upscale Image", variant="primary", size="lg")
|
| 565 |
+
|
| 566 |
with gr.Accordion("Advanced Settings", open=False):
|
|
|
|
| 567 |
unload_btn = gr.Button("Unload All Models (Free RAM)", variant="secondary")
|
| 568 |
+
system_info = gr.Label(value=get_system_usage(), label="System Status")
|
|
|
|
|
|
|
| 569 |
|
| 570 |
+
with gr.Column(scale=1, min_width=300):
|
| 571 |
+
output_image = gr.Image(type="filepath", label="Upscaled Result", height=400)
|
| 572 |
+
logs_output = gr.TextArea(label="Execution Logs", interactive=False, lines=8)
|
| 573 |
|
| 574 |
# Event Wiring
|
| 575 |
submit_btn.click(
|
| 576 |
fn=process_image,
|
| 577 |
+
inputs=[input_image, model_selector, output_format],
|
| 578 |
outputs=[output_image, logs_output, system_info]
|
| 579 |
)
|
| 580 |
|
|
|
|
| 583 |
inputs=[],
|
| 584 |
outputs=[logs_output, system_info]
|
| 585 |
)
|
|
|
|
|
|
|
|
|
|
| 586 |
|
| 587 |
+
iface.launch()
|
requirements.txt
CHANGED
|
@@ -1,7 +1,4 @@
|
|
| 1 |
torch
|
| 2 |
-
diffusers
|
| 3 |
-
transformers
|
| 4 |
-
accelerate
|
| 5 |
scipy
|
| 6 |
pillow
|
| 7 |
gradio
|
|
@@ -14,4 +11,4 @@ onnxruntime
|
|
| 14 |
basicsr
|
| 15 |
realesrgan
|
| 16 |
openvino
|
| 17 |
-
optimum
|
|
|
|
| 1 |
torch
|
|
|
|
|
|
|
|
|
|
| 2 |
scipy
|
| 3 |
pillow
|
| 4 |
gradio
|
|
|
|
| 11 |
basicsr
|
| 12 |
realesrgan
|
| 13 |
openvino
|
| 14 |
+
optimum
|